@reslide-dev/mdx 0.0.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.d.mts +61 -3273
  2. package/dist/index.mjs +129 -80
  3. package/package.json +23 -22
package/dist/index.mjs CHANGED
@@ -1,7 +1,12 @@
1
- import remarkFrontmatter from "remark-frontmatter";
2
1
  import { visit } from "unist-util-visit";
3
2
  import { compile } from "@mdx-js/mdx";
3
+ import rehypeShiki from "@shikijs/rehype";
4
+ import { transformerNotationHighlight } from "@shikijs/transformers";
5
+ import rehypeKatex from "rehype-katex";
4
6
  import remarkDirective from "remark-directive";
7
+ import remarkFrontmatter from "remark-frontmatter";
8
+ import remarkGfm from "remark-gfm";
9
+ import remarkMath from "remark-math";
5
10
  //#region src/remark-slides.ts
6
11
  function parseYamlString(value) {
7
12
  const options = {};
@@ -40,11 +45,10 @@ function tryExtractOptionsFromNodes(nodes) {
40
45
  * Remark plugin that splits MDX content at `---` (thematic breaks)
41
46
  * into `<Slide>` components wrapped in a `<Deck>`.
42
47
  *
43
- * Internally enables remark-frontmatter for the first YAML block.
48
+ * Requires remark-frontmatter to be added before this plugin in the pipeline.
44
49
  * Subsequent slide frontmatters are detected via setext heading pattern.
45
50
  */
46
51
  function remarkSlides() {
47
- this.use(remarkFrontmatter, ["yaml"]);
48
52
  return (tree) => {
49
53
  const slides = [];
50
54
  let current = [];
@@ -62,7 +66,6 @@ function remarkSlides() {
62
66
  const content = slideContent.slice(contentStart);
63
67
  if (content.length === 0 && Object.keys(options).length === 0) continue;
64
68
  const clickCount = countClickDirectives(content);
65
- const slideIndex = slideElements.length;
66
69
  const attrs = [];
67
70
  if (options.layout) attrs.push({
68
71
  type: "mdxJsxAttribute",
@@ -79,14 +82,6 @@ function remarkSlides() {
79
82
  type: "mdxJsxFlowElement",
80
83
  name: "ClickSteps",
81
84
  attributes: [{
82
- type: "mdxJsxAttribute",
83
- name: "slideIndex",
84
- value: {
85
- type: "mdxJsxAttributeValueExpression",
86
- value: String(slideIndex),
87
- data: { estree: createNumericExpression(slideIndex) }
88
- }
89
- }, {
90
85
  type: "mdxJsxAttribute",
91
86
  name: "count",
92
87
  value: {
@@ -142,94 +137,127 @@ function createNumericExpression(value) {
142
137
  * Remark plugin that converts `::click` directives (from remark-directive)
143
138
  * into `<Click>` MDX JSX components.
144
139
  *
145
- * Supports both leaf (`::click`) and container (`::: click ... :::`) forms.
140
+ * For leaf directives (`::click`), all subsequent sibling nodes until the
141
+ * next `::click` or `---` are gathered as children of the `<Click>`.
142
+ *
143
+ * For container directives (`::: click ... :::`), children are wrapped directly.
144
+ *
146
145
  * Auto-increments the `at` attribute for sequential click steps.
147
146
  */
148
147
  function remarkClick() {
149
148
  return (tree) => {
150
- let stepCounter = 0;
151
- visit(tree, (node, index, parent) => {
152
- if (node.type !== "leafDirective" && node.type !== "containerDirective") return;
153
- if (node.name !== "click") return;
154
- if (index == null || !parent) return;
155
- stepCounter++;
156
- const attrs = [{
157
- type: "mdxJsxAttribute",
158
- name: "at",
159
- value: {
160
- type: "mdxJsxAttributeValueExpression",
161
- value: String(stepCounter),
162
- data: { estree: {
163
- type: "Program",
164
- sourceType: "module",
165
- body: [{
166
- type: "ExpressionStatement",
167
- expression: {
168
- type: "Literal",
169
- value: stepCounter,
170
- raw: String(stepCounter)
171
- }
172
- }]
173
- } }
174
- }
175
- }];
176
- if (node.type === "leafDirective") {
177
- const siblings = parent.children;
178
- const gathered = [];
179
- let i = index + 1;
180
- while (i < siblings.length) {
181
- const sibling = siblings[i];
182
- if ((sibling.type === "leafDirective" || sibling.type === "containerDirective") && sibling.name === "click") break;
183
- if (sibling.type === "thematicBreak") break;
184
- gathered.push(siblings[i]);
185
- i++;
186
- }
187
- if (gathered.length > 0) siblings.splice(index + 1, gathered.length);
188
- parent.children[index] = {
189
- type: "mdxJsxFlowElement",
190
- name: "Click",
191
- attributes: attrs,
192
- children: gathered
193
- };
194
- return;
195
- }
196
- parent.children[index] = {
149
+ processNode(tree);
150
+ };
151
+ }
152
+ function isClickDirective(node) {
153
+ return (node.type === "leafDirective" || node.type === "containerDirective") && "name" in node && node.name === "click";
154
+ }
155
+ function processNode(node) {
156
+ for (const child of node.children) if ("children" in child && Array.isArray(child.children)) processNode(child);
157
+ const newChildren = [];
158
+ let stepCounter = 0;
159
+ let i = 0;
160
+ while (i < node.children.length) {
161
+ const child = node.children[i];
162
+ if (!isClickDirective(child)) {
163
+ newChildren.push(child);
164
+ i++;
165
+ continue;
166
+ }
167
+ stepCounter++;
168
+ if (child.type === "containerDirective") {
169
+ newChildren.push({
197
170
  type: "mdxJsxFlowElement",
198
171
  name: "Click",
199
- attributes: attrs,
200
- children: node.children
201
- };
172
+ attributes: [createAtAttribute(stepCounter)],
173
+ children: "children" in child ? child.children : []
174
+ });
175
+ i++;
176
+ continue;
177
+ }
178
+ const gathered = [];
179
+ i++;
180
+ while (i < node.children.length) {
181
+ const sibling = node.children[i];
182
+ if (isClickDirective(sibling)) break;
183
+ if (sibling.type === "thematicBreak") break;
184
+ gathered.push(sibling);
185
+ i++;
186
+ }
187
+ newChildren.push({
188
+ type: "mdxJsxFlowElement",
189
+ name: "Click",
190
+ attributes: [createAtAttribute(stepCounter)],
191
+ children: gathered
202
192
  });
193
+ }
194
+ node.children = newChildren;
195
+ }
196
+ function createAtAttribute(step) {
197
+ return {
198
+ type: "mdxJsxAttribute",
199
+ name: "at",
200
+ value: {
201
+ type: "mdxJsxAttributeValueExpression",
202
+ value: String(step),
203
+ data: { estree: {
204
+ type: "Program",
205
+ sourceType: "module",
206
+ body: [{
207
+ type: "ExpressionStatement",
208
+ expression: {
209
+ type: "Literal",
210
+ value: step,
211
+ raw: String(step)
212
+ }
213
+ }]
214
+ } }
215
+ }
203
216
  };
204
217
  }
205
218
  //#endregion
206
219
  //#region src/remark-mark.ts
220
+ const MARK_TYPES = [
221
+ "highlight",
222
+ "underline",
223
+ "circle"
224
+ ];
207
225
  /**
208
- * Remark plugin that converts `:mark[text]{.style}` directives
209
- * into `<Mark>` MDX JSX components.
226
+ * Remark plugin that converts mark directives into `<Mark>` MDX JSX components.
227
+ *
228
+ * Supports two syntax forms (both avoid `{}` for MDX compatibility):
210
229
  *
211
- * Supports styles: highlight, underline, circle
212
- * Supports color via additional class: .orange, .red, .blue, etc.
230
+ * 1. Type-based directives the directive name IS the mark type:
231
+ * `:highlight[text]` → `<Mark type="highlight">text</Mark>`
232
+ * `:underline[text]` → `<Mark type="underline">text</Mark>`
233
+ * `:circle[text]` → `<Mark type="circle">text</Mark>`
213
234
  *
214
- * Example: `:mark[important text]{.highlight.orange}`
215
- * Produces: `<Mark type="highlight" color="orange">important text</Mark>`
235
+ * 2. Type + color via hyphen:
236
+ * `:highlight-yellow[text]` `<Mark type="highlight" color="yellow">`
237
+ * `:underline-blue[text]` → `<Mark type="underline" color="blue">`
238
+ * `:circle-red[text]` → `<Mark type="circle" color="red">`
239
+ *
240
+ * 3. Legacy `:mark[text]{.type.color}` syntax (works in non-MDX contexts):
241
+ * `:mark[important]{.highlight.orange}` → `<Mark type="highlight" color="orange">`
216
242
  */
217
243
  function remarkMark() {
218
244
  return (tree) => {
219
245
  visit(tree, (node, index, parent) => {
220
246
  if (node.type !== "textDirective") return;
221
- if (node.name !== "mark") return;
222
247
  if (index == null || !parent) return;
223
- const classes = (node.attributes?.class ?? "").split(/\s+/).filter(Boolean);
224
- const markTypes = [
225
- "highlight",
226
- "underline",
227
- "circle"
228
- ];
229
- let type = "highlight";
248
+ let type;
230
249
  let color;
231
- for (const cls of classes) if (markTypes.includes(cls)) type = cls;
232
- else if (cls) color = cls;
250
+ if (node.name === "mark") {
251
+ const classes = (node.attributes?.class ?? "").split(/\s+/).filter(Boolean);
252
+ for (const cls of classes) if (MARK_TYPES.includes(cls)) type = cls;
253
+ else if (cls) color = cls;
254
+ type ??= "highlight";
255
+ } else {
256
+ const parsed = parseDirectiveName(node.name);
257
+ if (!parsed) return;
258
+ type = parsed.type;
259
+ color = parsed.color;
260
+ }
233
261
  const attrs = [{
234
262
  type: "mdxJsxAttribute",
235
263
  name: "type",
@@ -249,6 +277,17 @@ function remarkMark() {
249
277
  });
250
278
  };
251
279
  }
280
+ function parseDirectiveName(name) {
281
+ if (MARK_TYPES.includes(name)) return { type: name };
282
+ for (const markType of MARK_TYPES) if (name.startsWith(markType + "-")) {
283
+ const color = name.slice(markType.length + 1);
284
+ if (color) return {
285
+ type: markType,
286
+ color
287
+ };
288
+ }
289
+ return null;
290
+ }
252
291
  //#endregion
253
292
  //#region src/compile.ts
254
293
  /**
@@ -279,12 +318,22 @@ async function compileMdxSlides(source, options) {
279
318
  outputFormat: "function-body",
280
319
  remarkPlugins: [
281
320
  remarkDirective,
321
+ remarkGfm,
322
+ [remarkFrontmatter, ["yaml"]],
323
+ remarkMath,
282
324
  remarkSlides,
283
325
  remarkClick,
284
326
  remarkMark,
285
327
  ...options?.remarkPlugins ?? []
286
328
  ],
287
- rehypePlugins: options?.rehypePlugins ?? [],
329
+ rehypePlugins: [
330
+ rehypeKatex,
331
+ [rehypeShiki, {
332
+ theme: "github-dark",
333
+ transformers: [transformerNotationHighlight()]
334
+ }],
335
+ ...options?.rehypePlugins ?? []
336
+ ],
288
337
  providerImportSource: void 0
289
338
  });
290
339
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reslide-dev/mdx",
3
- "version": "0.0.1",
3
+ "version": "0.1.1",
4
4
  "description": "Remark/rehype plugins for reslide MDX preprocessing",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -11,39 +11,40 @@
11
11
  ".": "./dist/index.mjs",
12
12
  "./package.json": "./package.json"
13
13
  },
14
- "scripts": {
15
- "build": "vp pack",
16
- "dev": "vp pack --watch",
17
- "test": "vp test"
14
+ "publishConfig": {
15
+ "access": "public"
18
16
  },
19
17
  "dependencies": {
20
18
  "@mdx-js/mdx": "^3.1.1",
19
+ "@shikijs/rehype": "4.0.2",
20
+ "@shikijs/transformers": "4.0.2",
21
+ "rehype-katex": "^7.0.1",
21
22
  "remark-frontmatter": "^5.0.0",
23
+ "remark-gfm": "4.0.1",
24
+ "remark-math": "^6.0.0",
22
25
  "unist-util-visit": "^5.0.0"
23
26
  },
24
27
  "devDependencies": {
25
- "@types/mdast": "catalog:",
26
- "@types/unist": "catalog:",
28
+ "@types/mdast": "^4.0.4",
29
+ "@types/unist": "^3.0.3",
27
30
  "remark": "^15.0.0",
28
- "remark-directive": "^3.0.0",
29
- "remark-frontmatter": "catalog:",
31
+ "remark-directive": "4.0.0",
32
+ "remark-frontmatter": "^5.0.0",
30
33
  "remark-mdx": "^3.1.0",
31
- "unified": "catalog:",
32
- "vite-plus": "catalog:",
33
- "vitest": "catalog:"
34
+ "unified": "^11.0.5",
35
+ "vite-plus": "latest",
36
+ "vitest": "npm:@voidzero-dev/vite-plus-test@latest"
34
37
  },
35
38
  "peerDependencies": {
36
- "remark-directive": "^3.0.0"
37
- },
38
- "publishConfig": {
39
- "access": "public"
39
+ "remark-directive": "^4.0.0"
40
40
  },
41
41
  "inlinedDependencies": {
42
42
  "@types/mdast": "4.0.4",
43
- "@types/unist": "3.0.3",
44
- "trough": "2.2.0",
45
- "unified": "11.0.5",
46
- "vfile": "6.0.3",
47
- "vfile-message": "4.0.3"
43
+ "@types/unist": "3.0.3"
44
+ },
45
+ "scripts": {
46
+ "build": "vp pack",
47
+ "dev": "vp pack --watch",
48
+ "test": "vp test"
48
49
  }
49
- }
50
+ }