Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/components/SidebarItem/SidebarItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import list2Tree from "../../utilities/list2Tree/index.js";
* @returns {boolean}
*/
function isOpen(currentPage, url) {
return new RegExp(`${currentPage}/?$`).test(url);
const normalize = (value = "") => value.replace(/\/?$/, "/");
return normalize(url) === normalize(currentPage);
}

/**
Expand Down
12 changes: 12 additions & 0 deletions src/components/SidebarItem/SidebarItem.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,18 @@ describe("SidebarItem", () => {
expect(wrapper.getAttribute("data-open")).toBe("true");
});

it("matches URLs with regexp characters literally", () => {
const { container } = renderWithRouter(
<SidebarItem
{...defaultProps}
currentPage="/api/v3.0/[draft]"
url="/api/v3.0/[draft]/"
/>,
);
const wrapper = container.firstChild;
expect(wrapper.getAttribute("data-open")).toBe("true");
});

it("toggles open state when chevron button is clicked", () => {
const anchors = [
{
Expand Down
16 changes: 7 additions & 9 deletions src/remark-plugins/remark-refractor/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,14 @@ function attacher({ include, exclude } = {}) {
try {
data.hChildren = refractor.highlight(node.value, lang).children;
for (const child of data.hChildren) {
if (
child.properties &&
child.properties.className.includes("keyword")
) {
if (child.children[1]) {
child.properties.componentname = child.children[1].value.trim();
if (child.properties?.className?.includes("keyword")) {
const componentName = child.children?.[1]?.value?.trim();
if (componentName) {
child.properties.componentname = componentName;
}
if (child.children[2]) {
child.properties.url =
child.children[2].children[0].value.replaceAll('"', "");
const url = child.children?.[2]?.children?.[0]?.value;
if (url) {
child.properties.url = url.replaceAll('"', "");
}
}
}
Expand Down
18 changes: 8 additions & 10 deletions src/utilities/content-tree-enhancers.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,7 @@ export const enhance = (tree, options) => {
.use(extractAnchors, { anchors, levels: 3 })
.use(remarkRemoveHeadingId)
.use(remarkHtml)
.process(content, (err) => {
if (err) {
throw err;
}
});
.processSync(content);

tree.anchors = anchors;

Expand All @@ -73,14 +69,16 @@ export const enhance = (tree, options) => {

const isBlogItem = normalizedPath.includes("/blog/");
if (isBlogItem) {
const teaser = (body || "")
const teaserLines = (body || "")
.split("\n")
.filter((line) => line.trim() && !line.trim().startsWith("#"))
.filter((line) => line.trim() && !line.trim().startsWith("#"));
const teaserText = teaserLines
.slice(0, 3)
.join(" ")
.replaceAll(/\[([^\]]+)\]\([^)]+\)/g, "$1") // Strip markdown links but keep text
.slice(0, 240);
tree.teaser = `${teaser}...`;
.replaceAll(/\[([^\]]+)\]\([^)]+\)/g, "$1"); // Strip markdown links but keep text
const teaserContent = teaserText.slice(0, 240);
const isTruncated = teaserLines.length > 3 || teaserText.length > 240;
tree.teaser = isTruncated ? `${teaserContent}...` : teaserContent;
}

Object.assign(
Expand Down
43 changes: 42 additions & 1 deletion src/utilities/content-tree-enhancers.test.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
// eslint-disable-next-line import/no-extraneous-dependencies
import { describe, expect } from "@jest/globals";
import { restructure } from "./content-tree-enhancers.mjs";
import { enhance, restructure } from "./content-tree-enhancers.mjs";

describe("restructure", () => {
it("applies filter result back to children array", () => {
Expand Down Expand Up @@ -55,3 +58,41 @@ describe("restructure", () => {
expect(root.children.map((item) => item.title)).toEqual(["API", "Guides"]);
});
});

describe("enhance", () => {
const createBlogTree = (body) => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "webpack-blog-"));
const blogDir = path.join(root, "blog");
fs.mkdirSync(blogDir);
const filePath = path.join(blogDir, "example.mdx");
fs.writeFileSync(filePath, `---\ntitle: Example\n---\n\n${body}`);

return {
root,
tree: {
type: "file",
path: filePath,
extension: ".mdx",
name: "example.mdx",
},
};
};

it("does not append an ellipsis to an untruncated blog teaser", () => {
const { root, tree } = createBlogTree("Short body.");

enhance(tree, { dir: root });

expect(tree.teaser).toBe("Short body.");
});

it("appends an ellipsis to truncated blog teasers", () => {
const { root, tree } = createBlogTree(
["First line.", "Second line.", "Third line.", "Fourth line."].join("\n"),
);

enhance(tree, { dir: root });

expect(tree.teaser).toBe("First line. Second line. Third line....");
});
});
10 changes: 4 additions & 6 deletions src/utilities/content-utils.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,11 @@ export const getPageTitle = (tree, path) => {
// non page found
if (!page) return "webpack";

if (page) {
if (path.includes("/printable")) {
return "Combined printable page | webpack";
}
if (path === "/") return page.title || "webpack";
return `${page.title} | webpack`;
if (path.includes("/printable")) {
return "Combined printable page | webpack";
}
if (path === "/") return page.title || "webpack";
return `${page.title} | webpack`;
};

export const getPageDescription = (tree, path) => {
Expand Down
8 changes: 6 additions & 2 deletions src/utilities/flatten-content-tree.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ export default (tree) => {
}

if ("children" in node) {
node.children.map(crawl);
for (const child of node.children) {
crawl(child);
}
}
};

tree.children.map(crawl);
for (const child of tree.children) {
crawl(child);
}

return paths;
};
35 changes: 14 additions & 21 deletions src/utilities/process-readme.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,10 @@ function linkFixerFactory(sourceUrl) {

// Lowercase all fragment links, since markdown generators do the same
if (href.includes("#")) {
const [urlPath, urlFragment] = href.split("#");

href = `${urlPath}#${urlFragment.toLowerCase()}`;
const fragmentIndex = href.indexOf("#");
href = `${href.slice(0, fragmentIndex)}#${href
.slice(fragmentIndex + 1)
.toLowerCase()}`;
}

if (oldHref !== href) {
Expand All @@ -79,16 +80,6 @@ function linkFixerFactory(sourceUrl) {
};
}

function getMatches(string, regex) {
const matches = [];
let match;

while ((match = regex.exec(string))) {
matches.push(match);
}
return matches;
}

export default function processREADME(body, options = {}) {
let processingString = body
// close <img> tags
Expand Down Expand Up @@ -141,10 +132,11 @@ export default function processREADME(body, options = {}) {
);

// find the loaders links
const loaderMatches = getMatches(
processingString,
/https?:\/\/github.com\/(webpack|webpack-contrib)\/([-A-za-z0-9]+-loader\/?)([)"])/g,
);
const loaderMatches = [
...processingString.matchAll(
/https?:\/\/github.com\/(webpack|webpack-contrib)\/([-A-za-z0-9]+-loader\/?)([)"])/g,
),
];
// dont make relative links for excluded loaders
for (const match of loaderMatches) {
if (!excludedLoaders.includes(`${match[1]}/${match[2]}`)) {
Expand All @@ -155,10 +147,11 @@ export default function processREADME(body, options = {}) {
}
}

const pluginMatches = getMatches(
processingString,
/https?:\/\/github.com\/(webpack|webpack-contrib)\/([-A-za-z0-9]+-plugin\/?)([)"])/g,
);
const pluginMatches = [
...processingString.matchAll(
/https?:\/\/github.com\/(webpack|webpack-contrib)\/([-A-za-z0-9]+-plugin\/?)([)"])/g,
),
];
// dont make relative links for excluded loaders
for (const match of pluginMatches) {
if (!excludedPlugins.includes(`${match[1]}/${match[2]}`)) {
Expand Down
11 changes: 11 additions & 0 deletions src/utilities/process-readme.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,17 @@ describe("processReadme", () => {
);
});

it("preserves additional hash fragments when lowercasing anchors", () => {
const options = {
source:
"https://raw.githubusercontent.com/webpack/postcss-loader/main/README.md",
};
const loaderMDData = "[Example](https://example.com/page#Section#Nested)";
expect(processReadme(loaderMDData, options)).toBe(
"[Example](https://example.com/page#section#nested)",
);
});

it("should preserve comments inside code blocks", () => {
const options = {
source:
Expand Down
Loading