Skip to content

Commit d502b6e

Browse files
authored
Filter out opensource zudoku mode content (#833)
* Filter out opensource zudoku mode content * Additional redirects
1 parent 41e95a1 commit d502b6e

File tree

3 files changed

+154
-1
lines changed

3 files changed

+154
-1
lines changed

src/remark-if.ts

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import type { Parent, Root } from "mdast";
2+
import type { ContainerDirective } from "mdast-util-directive";
3+
import type { Plugin } from "unified";
4+
import type { Node } from "unist";
5+
import { SKIP, visit } from "unist-util-visit";
6+
7+
interface RemarkIfOptions {
8+
/**
9+
* The current mode to use for filtering content.
10+
* Content with a different mode will be removed.
11+
*/
12+
mode?: string;
13+
}
14+
15+
interface DirectiveNode extends ContainerDirective {
16+
attributes?: {
17+
mode?: string;
18+
};
19+
}
20+
21+
/**
22+
* A remark plugin that filters out content based on the mode attribute.
23+
*
24+
* Syntax: `::if{mode=opensource}...content...::`
25+
*
26+
* If the mode matches the configured mode, the content is kept (but the directive wrapper is removed).
27+
* If the mode doesn't match, the entire directive and its content is removed.
28+
*
29+
* @example
30+
* ```ts
31+
* import { remarkIf } from './remark-if.js';
32+
*
33+
* export default {
34+
* // cSpell:ignore opensource
35+
* remarkPlugins: [[remarkIf, { mode: 'opensource' }]],
36+
* };
37+
* ```
38+
*/
39+
export const remarkIf: Plugin<[RemarkIfOptions?], Root> = (options = {}) => {
40+
return (tree: Root) => {
41+
visit(tree, (node: Node, index?: number, parent?: Parent) => {
42+
// Handle both containerDirective (:::) and leafDirective (::) with name "if"
43+
if (
44+
(node.type !== "containerDirective" && node.type !== "leafDirective") ||
45+
(node as DirectiveNode).name !== "if" ||
46+
parent == null ||
47+
index == null
48+
) {
49+
return;
50+
}
51+
52+
const directive = node as DirectiveNode;
53+
54+
// Get the mode attribute from the directive
55+
const modeAttribute = directive.attributes?.mode;
56+
57+
// Find the closing "::" marker (for leafDirective) or handle containerDirective
58+
const isLeafDirective = node.type === "leafDirective";
59+
let closingIndex = -1;
60+
61+
if (isLeafDirective) {
62+
// For leaf directives, find the paragraph containing only "::"
63+
for (let i = index + 1; i < parent.children.length; i++) {
64+
const nextNode = parent.children[i];
65+
if (nextNode.type !== "paragraph" || !("children" in nextNode)) {
66+
continue;
67+
}
68+
const firstChild = nextNode.children?.[0];
69+
if (
70+
nextNode.children?.length === 1 &&
71+
firstChild &&
72+
firstChild.type === "text" &&
73+
"value" in firstChild &&
74+
typeof firstChild.value === "string" &&
75+
firstChild.value.trim() === "::"
76+
) {
77+
closingIndex = i;
78+
break;
79+
}
80+
}
81+
82+
// Fail if no closing "::" was found for leaf directive
83+
if (closingIndex === -1) {
84+
const position = directive.position
85+
? ` at line ${directive.position.start.line}`
86+
: "";
87+
throw new Error(
88+
`Unclosed ::if directive${position}. Leaf directives (::if) must be closed with a matching "::".`,
89+
);
90+
}
91+
}
92+
93+
if (!modeAttribute) {
94+
// If no mode attribute, keep the content but remove the directive wrapper
95+
if (isLeafDirective && closingIndex > -1) {
96+
// Remove the closing "::" and the directive, keep content in between
97+
parent.children.splice(closingIndex, 1);
98+
parent.children.splice(index, 1);
99+
return [SKIP, index];
100+
} else if (directive.children && directive.children.length > 0) {
101+
parent.children.splice(index, 1, ...directive.children);
102+
return [SKIP, index];
103+
} else {
104+
parent.children.splice(index, 1);
105+
return [SKIP, index];
106+
}
107+
}
108+
109+
// If the mode matches, keep the content but remove the directive wrapper and closing "::"
110+
if (modeAttribute === options.mode) {
111+
if (isLeafDirective && closingIndex > -1) {
112+
// Remove the closing "::" and the directive, keep content in between
113+
parent.children.splice(closingIndex, 1);
114+
parent.children.splice(index, 1);
115+
return [SKIP, index];
116+
} else if (directive.children && directive.children.length > 0) {
117+
parent.children.splice(index, 1, ...directive.children);
118+
return [SKIP, index];
119+
} else {
120+
parent.children.splice(index, 1);
121+
return [SKIP, index];
122+
}
123+
}
124+
125+
// If the mode doesn't match, remove the directive and all content until the closing "::"
126+
if (isLeafDirective && closingIndex > -1) {
127+
// Remove from directive to closing "::" (inclusive)
128+
parent.children.splice(index, closingIndex - index + 1);
129+
} else {
130+
// For container directives or when no closing found, just remove the directive
131+
parent.children.splice(index, 1);
132+
}
133+
return [SKIP, index];
134+
});
135+
};
136+
};

vercel.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1270,7 +1270,7 @@
12701270
},
12711271
{
12721272
"source": "/docs/mcp-server/mcp-server{/}?",
1273-
"destination": "/mcp-server/introduction",
1273+
"destination": "/docs/mcp-server/introduction",
12741274
"permanent": true
12751275
},
12761276
{
@@ -1283,10 +1283,20 @@
12831283
"destination": "/docs/mcp-server/tools",
12841284
"permanent": true
12851285
},
1286+
{
1287+
"source": "/docs/handlers/mcp-server-custom-tools{/}?",
1288+
"destination": "/docs/mcp-server/custom-tools",
1289+
"permanent": true
1290+
},
12861291
{
12871292
"source": "/docs/handlers/mcp-server-resources{/}?",
12881293
"destination": "/docs/mcp-server/resources",
12891294
"permanent": true
1295+
},
1296+
{
1297+
"source": "/docs/handlers/mcp-server-prompts{/}?",
1298+
"destination": "/docs/mcp-server/prompts",
1299+
"permanent": true
12901300
}
12911301
],
12921302
"rewrites": [

zudoku.build.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
import { removeExtensions } from "zudoku/processors/removeExtensions";
22
import type { ZudokuBuildConfig } from "zudoku";
3+
import type { PluggableList } from "unified";
4+
import { remarkIf } from "./src/remark-if.js";
5+
36
const buildConfig: ZudokuBuildConfig = {
7+
remarkPlugins: (defaultPlugins: PluggableList) => [
8+
...defaultPlugins,
9+
[remarkIf, { mode: "zuplo" }],
10+
],
411
processors: [
512
// Remove specific extensions
613
removeExtensions({

0 commit comments

Comments
 (0)