mirror of
https://github.com/zulip/zulip.git
synced 2025-10-24 08:33:43 +00:00
help-beta: Merge lists of same type adjacent to each other.
Fixes #31252. One of our major use cases for file imports is to have bullet points as partials to import at different places in the project. But when importing the file with Astro, it creates its own lists. So we merge lists together if they have nothing but whitespace between them. There were some talks to use a component called FlattenList that would flatten the list inside it, but that would also flatten lists that were nested on purpose. This approach while feeling a bit hacky would not flatten nested lists.
This commit is contained in:
committed by
Tim Abbott
parent
c0a2b2a31d
commit
b813d868a7
@@ -12,7 +12,10 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/check": "^0.9.3",
|
"@astrojs/check": "^0.9.3",
|
||||||
"@astrojs/starlight": "^0.33.0",
|
"@astrojs/starlight": "^0.33.0",
|
||||||
|
"@types/hast": "^3.0.4",
|
||||||
"astro": "^5.1.2",
|
"astro": "^5.1.2",
|
||||||
|
"hast-util-from-html": "^2.0.3",
|
||||||
|
"hast-util-to-html": "^9.0.5",
|
||||||
"sharp": "^0.34.1",
|
"sharp": "^0.34.1",
|
||||||
"typescript": "^5.4.5"
|
"typescript": "^5.4.5"
|
||||||
}
|
}
|
||||||
|
|||||||
82
help-beta/src/middleware.ts
Normal file
82
help-beta/src/middleware.ts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import {defineMiddleware} from "astro:middleware";
|
||||||
|
import type {Element, Root, RootContent} from "hast";
|
||||||
|
import {fromHtml} from "hast-util-from-html";
|
||||||
|
import {toHtml} from "hast-util-to-html";
|
||||||
|
|
||||||
|
function isList(node: Element): boolean {
|
||||||
|
return node.tagName === "ol" || node.tagName === "ul";
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function traverses the HTML tree and merges lists of the same
|
||||||
|
// type if they are adjacent to each other. This is kinda a hack to
|
||||||
|
// make file imports work within lists. One of our major use cases
|
||||||
|
// for file imports is to have bullet points as partials to import at
|
||||||
|
// different places in the project. But when importing the file with
|
||||||
|
// Astro, it creates its own lists. So we merge lists together if they
|
||||||
|
// have nothing but whitespace between them.
|
||||||
|
function mergeAdjacentListsOfSameType(tree: Root): Root {
|
||||||
|
function recursiveMergeAdjacentLists(node: Element | Root): void {
|
||||||
|
if (!node.children) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modifiedChildren: RootContent[] = [];
|
||||||
|
let currentIndex = 0;
|
||||||
|
|
||||||
|
while (currentIndex < node.children.length) {
|
||||||
|
const currentChild = node.children[currentIndex]!;
|
||||||
|
|
||||||
|
if (currentChild.type === "element" && isList(currentChild)) {
|
||||||
|
const mergedList = structuredClone(currentChild);
|
||||||
|
let lookaheadIndex = currentIndex + 1;
|
||||||
|
|
||||||
|
while (lookaheadIndex < node.children.length) {
|
||||||
|
const lookaheadChild = node.children[lookaheadIndex]!;
|
||||||
|
|
||||||
|
if (lookaheadChild.type === "element" && isList(lookaheadChild)) {
|
||||||
|
if (lookaheadChild.tagName === currentChild.tagName) {
|
||||||
|
mergedList.children.push(...lookaheadChild.children);
|
||||||
|
}
|
||||||
|
lookaheadIndex += 1;
|
||||||
|
} else if (
|
||||||
|
lookaheadChild.type === "text" &&
|
||||||
|
/^\s*$/.test(lookaheadChild.value)
|
||||||
|
) {
|
||||||
|
// Whitespace should be allowed in between lists.
|
||||||
|
lookaheadIndex += 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modifiedChildren.push(mergedList);
|
||||||
|
currentIndex = lookaheadIndex;
|
||||||
|
} else {
|
||||||
|
modifiedChildren.push(currentChild);
|
||||||
|
currentIndex += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node.children = modifiedChildren;
|
||||||
|
for (const child of node.children) {
|
||||||
|
if (child.type === "element") {
|
||||||
|
recursiveMergeAdjacentLists(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
recursiveMergeAdjacentLists(tree);
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const onRequest = defineMiddleware(async (_context, next) => {
|
||||||
|
const response = await next();
|
||||||
|
const html = await response.text();
|
||||||
|
const tree = fromHtml(html);
|
||||||
|
const result = toHtml(mergeAdjacentListsOfSameType(tree));
|
||||||
|
|
||||||
|
return new Response(result, {
|
||||||
|
status: 200,
|
||||||
|
headers: response.headers,
|
||||||
|
});
|
||||||
|
});
|
||||||
9
pnpm-lock.yaml
generated
9
pnpm-lock.yaml
generated
@@ -503,9 +503,18 @@ importers:
|
|||||||
'@astrojs/starlight':
|
'@astrojs/starlight':
|
||||||
specifier: ^0.33.0
|
specifier: ^0.33.0
|
||||||
version: 0.33.0(astro@5.6.1(@types/node@22.14.0)(jiti@1.21.7)(rollup@4.39.0)(sass@1.86.3)(terser@5.39.0)(typescript@5.8.3)(yaml@2.7.1))
|
version: 0.33.0(astro@5.6.1(@types/node@22.14.0)(jiti@1.21.7)(rollup@4.39.0)(sass@1.86.3)(terser@5.39.0)(typescript@5.8.3)(yaml@2.7.1))
|
||||||
|
'@types/hast':
|
||||||
|
specifier: ^3.0.4
|
||||||
|
version: 3.0.4
|
||||||
astro:
|
astro:
|
||||||
specifier: ^5.1.2
|
specifier: ^5.1.2
|
||||||
version: 5.6.1(@types/node@22.14.0)(jiti@1.21.7)(rollup@4.39.0)(sass@1.86.3)(terser@5.39.0)(typescript@5.8.3)(yaml@2.7.1)
|
version: 5.6.1(@types/node@22.14.0)(jiti@1.21.7)(rollup@4.39.0)(sass@1.86.3)(terser@5.39.0)(typescript@5.8.3)(yaml@2.7.1)
|
||||||
|
hast-util-from-html:
|
||||||
|
specifier: ^2.0.3
|
||||||
|
version: 2.0.3
|
||||||
|
hast-util-to-html:
|
||||||
|
specifier: ^9.0.5
|
||||||
|
version: 9.0.5
|
||||||
sharp:
|
sharp:
|
||||||
specifier: ^0.34.1
|
specifier: ^0.34.1
|
||||||
version: 0.34.1
|
version: 0.34.1
|
||||||
|
|||||||
@@ -176,6 +176,10 @@ def run() -> None:
|
|||||||
include_source_dir = os.path.join(BASE_DIR, "help/include")
|
include_source_dir = os.path.join(BASE_DIR, "help/include")
|
||||||
include_destination_dir = os.path.join(BASE_DIR, "help-beta/src/content/docs/include")
|
include_destination_dir = os.path.join(BASE_DIR, "help-beta/src/content/docs/include")
|
||||||
shutil.copytree(include_source_dir, include_destination_dir)
|
shutil.copytree(include_source_dir, include_destination_dir)
|
||||||
|
|
||||||
|
# We do not want Astro to render these include files as standalone
|
||||||
|
# files, prefixing them with an underscore accomplishes that.
|
||||||
|
# https://docs.astro.build/en/guides/routing/#excluding-pages
|
||||||
for name in os.listdir(include_destination_dir):
|
for name in os.listdir(include_destination_dir):
|
||||||
os.rename(
|
os.rename(
|
||||||
os.path.join(include_destination_dir, name),
|
os.path.join(include_destination_dir, name),
|
||||||
|
|||||||
@@ -49,4 +49,4 @@ API_FEATURE_LEVEL = 378
|
|||||||
# historical commits sharing the same major version, in which case a
|
# historical commits sharing the same major version, in which case a
|
||||||
# minor version bump suffices.
|
# minor version bump suffices.
|
||||||
|
|
||||||
PROVISION_VERSION = (325, 0) # bumped 2025-04-09 to upgrade JavaScript dependencies
|
PROVISION_VERSION = (325, 1) # bumped 2025-04-16 to add hast dependencies to help-beta
|
||||||
|
|||||||
Reference in New Issue
Block a user