@temelj/mdx 0.11.1 → 0.12.0

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.
package/README.md CHANGED
@@ -37,8 +37,7 @@ $ deno add jsr:@temelj/mdx # or jsr add @temelj/mdx
37
37
 
38
38
  ## Compile
39
39
 
40
- ```ts
41
- import { expect, test } from "vitest";
40
+ ```ts ignore
42
41
  import {
43
42
  type HastElement,
44
43
  headingIdPlugin,
@@ -46,7 +45,7 @@ import {
46
45
  syntaxHighlightPlugin,
47
46
  treeProcessorPlugin,
48
47
  } from "@temelj/mdx";
49
- import * as z from "zod";
48
+ import { z } from "zod";
50
49
 
51
50
  const compiler = new MdxCompiler()
52
51
  .withRehypePlugin(headingIdPlugin, {
@@ -73,7 +72,7 @@ title: Demo
73
72
  This will compile MDX to a render-able artifact.
74
73
  With '@temelj/mdx-react' you can render it in an React app.
75
74
 
76
- You can validate the frontmatter with a zod schema.
75
+ You can validate the frontmatter with any Standard Schema-compatible validator.
77
76
 
78
77
  There are a few useful plugins:
79
78
  - Use 'headingIdPlugin' to update heading tags (h1, h2, etc.) with IDs generated by slugging their text content.
@@ -88,6 +87,11 @@ whoamai
88
87
  @temelj/mdx
89
88
  \`\`\`
90
89
  `.trim();
90
+
91
+ const frontmatterSchema = z.object({
92
+ title: z.string(),
93
+ });
94
+
91
95
  const artifact = await compiler.compile(
92
96
  source,
93
97
  {
@@ -97,16 +101,13 @@ const artifact = await compiler.compile(
97
101
  [
98
102
  treeProcessorPlugin,
99
103
  {
100
- process: (node: HastElement) =>
101
- assert(typeof node.type === "string"),
104
+ process: (node: HastElement) => assert(typeof node.type === "string"),
102
105
  },
103
106
  ],
104
107
  ],
105
108
  },
106
109
  },
107
- z.object({
108
- title: z.string(),
109
- }),
110
+ frontmatterSchema,
110
111
  );
111
112
  assert(artifact.compiled !== undefined);
112
113
  assertEquals(artifact.frontmatter, {
@@ -2,7 +2,7 @@ import { HastNode as Node } from "./types.mjs";
2
2
  import { CompileOptions } from "@mdx-js/mdx";
3
3
  import remarkFrontmatterPlugin from "remark-frontmatter";
4
4
  import remarkGfmPlugin from "remark-gfm";
5
- import { z } from "zod";
5
+ import { StandardSchemaV1 } from "@standard-schema/spec";
6
6
  import { Plugin } from "unified";
7
7
 
8
8
  //#region src/compiler.d.ts
@@ -37,6 +37,8 @@ interface MdxMessage {
37
37
  place?: MdxMessagePlace | undefined;
38
38
  sourceLine?: string | undefined;
39
39
  snippet?: string | undefined;
40
+ sourcePointer?: string | undefined;
41
+ hint?: string | undefined;
40
42
  cause?: {
41
43
  name?: string | undefined;
42
44
  message: string;
@@ -60,6 +62,8 @@ declare class MdxCompileError extends Error {
60
62
  readonly place?: MdxMessagePlace | undefined;
61
63
  readonly sourceLine?: string | undefined;
62
64
  readonly snippet?: string | undefined;
65
+ readonly sourcePointer?: string | undefined;
66
+ readonly hint?: string | undefined;
63
67
  readonly diagnostics: MdxMessage[];
64
68
  constructor(reason: string, options?: {
65
69
  source?: string | undefined;
@@ -69,6 +73,9 @@ declare class MdxCompileError extends Error {
69
73
  place?: MdxMessagePlace | undefined;
70
74
  sourceLine?: string | undefined;
71
75
  snippet?: string | undefined;
76
+ sourcePointer?: string | undefined;
77
+ hint?: string | undefined;
78
+ causeMessage?: string | undefined;
72
79
  diagnostics?: MdxMessage[] | undefined;
73
80
  cause?: unknown;
74
81
  });
@@ -83,10 +90,8 @@ declare class MdxCompiler {
83
90
  constructor(mdxOptions?: CompileOptions);
84
91
  withRemarkPlugin<TOptions>(plugin: Plugin<[TOptions?], Node, Node>, options?: TOptions): MdxCompiler;
85
92
  withRehypePlugin<TOptions>(plugin: Plugin<[TOptions?], Node, Node>, options?: TOptions): MdxCompiler;
86
- compile<TFrontmatterSchema extends z.ZodSchema>(source: MdxSource, {
87
- frontmatterOnly,
88
- mdxOptions
89
- }?: MdxCompileOptions, frontmatterSchema?: TFrontmatterSchema): Promise<MdxArtifact<z.output<TFrontmatterSchema>>>;
93
+ compile(source: MdxSource, options?: MdxCompileOptions): Promise<MdxArtifact<Record<string, unknown>>>;
94
+ compile<TFrontmatterSchema extends StandardSchemaV1>(source: MdxSource, options: MdxCompileOptions | undefined, frontmatterSchema: TFrontmatterSchema): Promise<MdxArtifact<StandardSchemaV1.InferOutput<TFrontmatterSchema>>>;
90
95
  }
91
96
  //#endregion
92
97
  export { MdxArtifact, MdxCompileError, MdxCompileOptions, MdxCompiler, MdxMessage, MdxMessagePlace, MdxMessagePoint, MdxSource, remarkFrontmatterPlugin, remarkGfmPlugin };
@@ -1 +1 @@
1
- {"version":3,"file":"compiler.d.mts","names":[],"sources":["../src/compiler.ts"],"mappings":";;;;;;;;;AAkBA;;KAAY,SAAA,YAAqB,UAAA;;;AAKjC;UAAiB,iBAAA;EACf,eAAA;EACA,UAAA,GAAa,cAAA;AAAA;AAAA,UAGE,eAAA;EACf,IAAA;EACA,MAAA;EACA,MAAA;AAAA;AAAA,UAGe,eAAA;EACf,KAAA,GAAQ,eAAA;EACR,GAAA,GAAM,eAAA;AAAA;AAAA,UAGS,UAAA;EACf,OAAA;EACA,MAAA;EACA,MAAA;EACA,MAAA;EACA,KAAA;EACA,IAAA;EACA,MAAA;EACA,KAAA,GAAQ,eAAA;EACR,UAAA;EACA,OAAA;EACA,KAAA;IACE,IAAA;IACA,OAAA;EAAA;AAAA;;;;;UAUa,WAAA,gBAA2B,MAAA;EAC1C,QAAA;EACA,WAAA,EAAa,YAAA;EACb,QAAA,EAAU,UAAA;AAAA;AAAA,cAGC,eAAA,SAAwB,KAAA;EAAA,SACnB,MAAA;EAAA,SACA,MAAA;EAAA,SACA,MAAA;EAAA,SACA,IAAA;EAAA,SACA,MAAA;EAAA,SACA,KAAA,GAAQ,eAAA;EAAA,SACR,UAAA;EAAA,SACA,OAAA;EAAA,SACA,WAAA,EAAa,UAAA;cAG3B,MAAA,UACA,OAAA;IACE,MAAA;IACA,MAAA;IACA,IAAA;IACA,MAAA;IACA,KAAA,GAAQ,eAAA;IACR,UAAA;IACA,OAAA;IACA,WAAA,GAAc,UAAA;IACd,KAAA;EAAA;AAAA;;;;cA+BO,WAAA;EAAA,iBACM,UAAA;EAAA,iBACA,aAAA;EAAA,iBACA,aAAA;cAEE,UAAA,GAAY,cAAA;EAMxB,gBAAA,UAAA,CACL,MAAA,EAAQ,MAAA,EAAQ,QAAA,IAAY,IAAA,EAAU,IAAA,GACtC,OAAA,GAAU,QAAA,GACT,WAAA;EASI,gBAAA,UAAA,CACL,MAAA,EAAQ,MAAA,EAAQ,QAAA,IAAY,IAAA,EAAU,IAAA,GACtC,OAAA,GAAU,QAAA,GACT,WAAA;EASU,OAAA,4BAAmC,CAAA,CAAE,SAAA,CAAA,CAChD,MAAA,EAAQ,SAAA;IACN,eAAA;IAAyB;EAAA,IAAc,iBAAA,EACzC,iBAAA,GAAoB,kBAAA,GACnB,OAAA,CAAQ,WAAA,CAAY,CAAA,CAAE,MAAA,CAAO,kBAAA;AAAA"}
1
+ {"version":3,"file":"compiler.d.mts","names":[],"sources":["../src/compiler.ts"],"mappings":";;;;;;;;;AAiBA;;KAAY,SAAA,YAAqB,UAAU;;AAAA;AAK3C;UAAiB,iBAAA;EACf,eAAA;EACA,UAAA,GAAa,cAAmB;AAAA;AAAA,UAGjB,eAAA;EACf,IAAA;EACA,MAAA;EACA,MAAA;AAAA;AAAA,UAGe,eAAA;EACf,KAAA,GAAQ,eAAA;EACR,GAAA,GAAM,eAAe;AAAA;AAAA,UAGN,UAAA;EACf,OAAA;EACA,MAAA;EACA,MAAA;EACA,MAAA;EACA,KAAA;EACA,IAAA;EACA,MAAA;EACA,KAAA,GAAQ,eAAe;EACvB,UAAA;EACA,OAAA;EACA,aAAA;EACA,IAAA;EACA,KAAA;IACE,IAAA;IACA,OAAA;EAAA;AAAA;;;;;UAea,WAAA,gBAA2B,MAAA;EAC1C,QAAA;EACA,WAAA,EAAa,YAAA;EACb,QAAA,EAAU,UAAA;AAAA;AAAA,cAGC,eAAA,SAAwB,KAAA;EAAA,SACnB,MAAA;EAAA,SACA,MAAA;EAAA,SACA,MAAA;EAAA,SACA,IAAA;EAAA,SACA,MAAA;EAAA,SACA,KAAA,GAAQ,eAAA;EAAA,SACR,UAAA;EAAA,SACA,OAAA;EAAA,SACA,aAAA;EAAA,SACA,IAAA;EAAA,SACA,WAAA,EAAa,UAAA;cAG3B,MAAA,UACA,OAAA;IACE,MAAA;IACA,MAAA;IACA,IAAA;IACA,MAAA;IACA,KAAA,GAAQ,eAAA;IACR,UAAA;IACA,OAAA;IACA,aAAA;IACA,IAAA;IACA,YAAA;IACA,WAAA,GAAc,UAAA;IACd,KAAA;EAAA;AAAA;;;;cAoCO,WAAA;EAAA,iBACM,UAAA;EAAA,iBACA,aAAA;EAAA,iBACA,aAAA;cAEE,UAAA,GAAY,cAAA;EAMxB,gBAAA,WACL,MAAA,EAAQ,MAAA,EAAQ,QAAA,IAAY,IAAA,EAAU,IAAA,GACtC,OAAA,GAAU,QAAA,GACT,WAAA;EASI,gBAAA,WACL,MAAA,EAAQ,MAAA,EAAQ,QAAA,IAAY,IAAA,EAAU,IAAA,GACtC,OAAA,GAAU,QAAA,GACT,WAAA;EASU,OAAA,CACX,MAAA,EAAQ,SAAA,EACR,OAAA,GAAU,iBAAA,GACT,OAAA,CAAQ,WAAA,CAAY,MAAA;EACV,OAAA,4BAAmC,gBAAA,EAC9C,MAAA,EAAQ,SAAA,EACR,OAAA,EAAS,iBAAA,cACT,iBAAA,EAAmB,kBAAA,GAClB,OAAA,CAAQ,WAAA,CAAY,gBAAA,CAAiB,WAAA,CAAY,kBAAA;AAAA"}
package/dist/compiler.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import { compile } from "@mdx-js/mdx";
2
+ import { validateStandardSchema } from "@temelj/standard-schema";
2
3
  import remarkFrontmatterPlugin from "remark-frontmatter";
3
4
  import remarkGfmPlugin from "remark-gfm";
4
5
  import { VFile } from "vfile";
@@ -13,6 +14,8 @@ var MdxCompileError = class extends Error {
13
14
  place;
14
15
  sourceLine;
15
16
  snippet;
17
+ sourcePointer;
18
+ hint;
16
19
  diagnostics;
17
20
  constructor(reason, options = {}) {
18
21
  super(formatDiagnosticMessage(reason, {
@@ -21,7 +24,10 @@ var MdxCompileError = class extends Error {
21
24
  line: options.line,
22
25
  column: options.column,
23
26
  sourceLine: options.sourceLine,
24
- snippet: options.snippet
27
+ snippet: options.snippet,
28
+ sourcePointer: options.sourcePointer,
29
+ hint: options.hint,
30
+ causeMessage: options.causeMessage
25
31
  }), options.cause === void 0 ? void 0 : { cause: options.cause });
26
32
  this.name = "MdxCompileError";
27
33
  this.reason = reason;
@@ -32,6 +38,8 @@ var MdxCompileError = class extends Error {
32
38
  this.place = options.place;
33
39
  this.sourceLine = options.sourceLine;
34
40
  this.snippet = options.snippet;
41
+ this.sourcePointer = options.sourcePointer;
42
+ this.hint = options.hint;
35
43
  this.diagnostics = options.diagnostics ?? [];
36
44
  }
37
45
  };
@@ -65,6 +73,7 @@ var MdxCompiler = class {
65
73
  path: "/source.mdx"
66
74
  });
67
75
  matter(vfile, { strip: true });
76
+ const sourceContext = createDiagnosticSourceContext(sourceText, toSourceText(vfile.value));
68
77
  const remarkPlugins = [
69
78
  ...this.remarkPlugins,
70
79
  ...this.mdxOptions?.remarkPlugins ?? [],
@@ -79,24 +88,25 @@ var MdxCompiler = class {
79
88
  if (!frontmatterOnly) try {
80
89
  compiled = (await compile(vfile, {
81
90
  outputFormat: "function-body",
82
- ...this.mdxOptions ?? {},
83
- ...mdxOptions ?? {},
91
+ ...this.mdxOptions,
92
+ ...mdxOptions,
84
93
  remarkPlugins,
85
94
  rehypePlugins
86
95
  })).value.toString();
87
96
  } catch (error) {
88
- throw toMdxCompileError(error, sourceText, vfile.messages);
97
+ throw toMdxCompileError(error, sourceContext, vfile.messages);
89
98
  }
90
- const frontmatter = frontmatterSchema === void 0 ? vfile.data.matter ?? {} : frontmatterSchema.parse(vfile.data.matter ?? {});
99
+ const frontmatter = frontmatterSchema === void 0 ? vfile.data.matter ?? {} : await validateStandardSchema(frontmatterSchema, vfile.data.matter ?? {}, "Frontmatter validation failed");
91
100
  return {
92
101
  compiled,
93
102
  frontmatter,
94
- messages: normalizeMessages(vfile.messages, sourceText)
103
+ messages: normalizeMessages(vfile.messages, sourceContext)
95
104
  };
96
105
  }
97
106
  };
98
- function toMdxCompileError(error, sourceText, diagnostics) {
99
- const details = getDiagnosticDetails(sourceText, getErrorPlace(error), error);
107
+ function toMdxCompileError(error, sourceContext, diagnostics) {
108
+ const details = getDiagnosticDetails(sourceContext, getErrorPlace(error), error);
109
+ const cause = getErrorCause(error);
100
110
  return new MdxCompileError(getErrorReason(error), {
101
111
  source: getErrorField(error, "source"),
102
112
  ruleId: getErrorField(error, "ruleId"),
@@ -105,13 +115,16 @@ function toMdxCompileError(error, sourceText, diagnostics) {
105
115
  place: details.place,
106
116
  sourceLine: details.sourceLine,
107
117
  snippet: details.snippet,
108
- diagnostics: normalizeMessages(diagnostics, sourceText),
118
+ sourcePointer: details.sourcePointer,
119
+ hint: details.hint,
120
+ causeMessage: cause?.message,
121
+ diagnostics: normalizeMessages(diagnostics, sourceContext),
109
122
  cause: error
110
123
  });
111
124
  }
112
- function normalizeMessages(messages, sourceText) {
125
+ function normalizeMessages(messages, sourceContext) {
113
126
  return messages.map((message) => {
114
- const details = getDiagnosticDetails(sourceText, toMessagePlace(message), message);
127
+ const details = getDiagnosticDetails(sourceContext, toMessagePlace(message), message);
115
128
  const reason = getErrorReason(message);
116
129
  const source = getErrorField(message, "source");
117
130
  const ruleId = getErrorField(message, "ruleId");
@@ -124,6 +137,8 @@ function normalizeMessages(messages, sourceText) {
124
137
  column: details.column,
125
138
  sourceLine: details.sourceLine,
126
139
  snippet: details.snippet,
140
+ sourcePointer: details.sourcePointer,
141
+ hint: details.hint,
127
142
  causeMessage: cause?.message
128
143
  }),
129
144
  reason,
@@ -135,6 +150,8 @@ function normalizeMessages(messages, sourceText) {
135
150
  place: details.place,
136
151
  sourceLine: details.sourceLine,
137
152
  snippet: details.snippet,
153
+ sourcePointer: details.sourcePointer,
154
+ hint: details.hint,
138
155
  cause
139
156
  };
140
157
  });
@@ -146,16 +163,21 @@ function formatDiagnosticMessage(reason, options) {
146
163
  options.line === void 0 ? void 0 : options.column === void 0 ? `at ${options.line}` : `at ${options.line}:${options.column}`
147
164
  ].filter(Boolean).join(" ")];
148
165
  if (options.snippet !== void 0) lines.push(`Source: ${options.snippet}`);
149
- if (options.sourceLine !== void 0 && options.sourceLine !== options.snippet) lines.push(`Line: ${options.sourceLine}`);
166
+ if (options.sourceLine !== void 0 && options.sourceLine !== options.snippet) {
167
+ lines.push(`Line: ${options.sourceLine}`);
168
+ if (options.sourcePointer !== void 0) lines.push(` ${options.sourcePointer}`);
169
+ }
150
170
  if (options.causeMessage !== void 0 && options.causeMessage !== "" && options.causeMessage !== reason) lines.push(`Cause: ${options.causeMessage}`);
171
+ if (options.hint !== void 0) lines.push(`Hint: ${options.hint}`);
151
172
  return lines.join("\n");
152
173
  }
153
- function getDiagnosticDetails(sourceText, place, error) {
154
- const line = place?.start?.line ?? getErrorNumberField(error, "line");
155
- const column = place?.start?.column ?? getErrorNumberField(error, "column");
156
- const normalizedPlace = line === void 0 && column === void 0 && place === void 0 ? void 0 : {
174
+ function getDiagnosticDetails(sourceContext, place, error) {
175
+ const compileLine = place?.start?.line ?? getErrorNumberField(error, "line");
176
+ let line = compileLine === void 0 ? void 0 : compileLine + sourceContext.lineOffset;
177
+ let column = place?.start?.column ?? getErrorNumberField(error, "column");
178
+ let normalizedPlace = mapPlaceToOriginalSource(place === void 0 && compileLine === void 0 && column === void 0 ? void 0 : {
157
179
  start: {
158
- line,
180
+ line: compileLine,
159
181
  column,
160
182
  offset: place?.start?.offset
161
183
  },
@@ -164,15 +186,61 @@ function getDiagnosticDetails(sourceText, place, error) {
164
186
  column: place.end.column,
165
187
  offset: place.end.offset
166
188
  }
167
- };
189
+ }, sourceContext.lineOffset);
190
+ let sourceLine = getSourceLine(sourceContext.originalSourceText, line);
191
+ let snippet = getSourceSnippet(sourceContext.originalSourceText, normalizedPlace);
192
+ let sourcePointer = getSourcePointer(column);
193
+ let hint;
194
+ const refinedExpressionContext = getMdxExpressionContext(sourceContext.originalSourceText, {
195
+ line,
196
+ column,
197
+ sourceLine,
198
+ snippet,
199
+ error
200
+ });
201
+ if (refinedExpressionContext !== void 0) {
202
+ line = refinedExpressionContext.line;
203
+ column = refinedExpressionContext.column;
204
+ sourceLine = refinedExpressionContext.sourceLine;
205
+ snippet = refinedExpressionContext.snippet;
206
+ sourcePointer = refinedExpressionContext.sourcePointer;
207
+ hint = refinedExpressionContext.hint;
208
+ normalizedPlace = {
209
+ start: {
210
+ line,
211
+ column,
212
+ offset: void 0
213
+ },
214
+ end: void 0
215
+ };
216
+ }
168
217
  return {
169
218
  line,
170
219
  column,
171
220
  place: normalizedPlace,
172
- sourceLine: getSourceLine(sourceText, line),
173
- snippet: getSourceSnippet(sourceText, normalizedPlace)
221
+ sourceLine,
222
+ snippet,
223
+ sourcePointer,
224
+ hint
225
+ };
226
+ }
227
+ function getMdxExpressionContext(sourceText, options) {
228
+ if (!shouldRefineMdxExpressionContext(options)) return;
229
+ const candidate = findSuspiciousBraceExpression(sourceText, options.line, options.column);
230
+ if (candidate === void 0) return;
231
+ return {
232
+ line: candidate.line,
233
+ column: candidate.column,
234
+ sourceLine: candidate.sourceLine,
235
+ snippet: candidate.snippet,
236
+ sourcePointer: getSourcePointer(candidate.column) ?? "^",
237
+ hint: getErrorCause(options.error)?.message === "Unexpected content after expression" ? "MDX treats `{...}` as JavaScript. If you meant literal braces in text, escape them or wrap the text in code." : void 0
174
238
  };
175
239
  }
240
+ function shouldRefineMdxExpressionContext(options) {
241
+ const trimmedSourceLine = options.sourceLine?.trim();
242
+ return isMdxExpressionError(options.error) && getErrorCause(options.error)?.message === "Unexpected content after expression" && (options.sourceLine === void 0 || options.sourceLine.trim() === "" || options.snippet === void 0) && !(trimmedSourceLine?.startsWith("{") && trimmedSourceLine.endsWith("}"));
243
+ }
176
244
  function getSourceLine(sourceText, line) {
177
245
  if (line === void 0 || line < 1) return;
178
246
  return sourceText.split(/\r?\n/u)[line - 1];
@@ -180,6 +248,12 @@ function getSourceLine(sourceText, line) {
180
248
  function getSourceSnippet(sourceText, place) {
181
249
  if (place?.start?.offset !== void 0 && place.end?.offset !== void 0) return sourceText.slice(place.start.offset, place.end.offset) || void 0;
182
250
  if (place?.start?.line === void 0 || place.start.column === void 0) return;
251
+ if (place.end === void 0) {
252
+ const line = sourceText.split(/\r?\n/u)[place.start.line - 1];
253
+ if (line === void 0) return;
254
+ const character = line.at(place.start.column - 1);
255
+ return character === void 0 || character.trim() === "" ? void 0 : character;
256
+ }
183
257
  const lines = sourceText.split(/\r?\n/u);
184
258
  const startLineIndex = place.start.line - 1;
185
259
  const endLineIndex = (place.end?.line ?? place.start.line) - 1;
@@ -194,6 +268,110 @@ function getSourceSnippet(sourceText, place) {
194
268
  }
195
269
  return selectedLines.join("\n") || void 0;
196
270
  }
271
+ function getSourcePointer(column) {
272
+ if (column === void 0 || column < 1) return;
273
+ return `${" ".repeat(column - 1)}^`;
274
+ }
275
+ function mapPlaceToOriginalSource(place, lineOffset) {
276
+ if (place === void 0) return;
277
+ return {
278
+ start: mapPointToOriginalSource(place.start, lineOffset),
279
+ end: mapPointToOriginalSource(place.end, lineOffset)
280
+ };
281
+ }
282
+ function mapPointToOriginalSource(point, lineOffset) {
283
+ if (point === void 0) return;
284
+ return {
285
+ line: point.line === void 0 ? void 0 : point.line + lineOffset,
286
+ column: point.column,
287
+ offset: lineOffset === 0 ? point.offset : void 0
288
+ };
289
+ }
290
+ function findSuspiciousBraceExpression(sourceText, line, column) {
291
+ const lines = sourceText.split(/\r?\n/u);
292
+ const searchLines = getNearbyLineNumbers(line, lines.length, 2);
293
+ let bestMatch;
294
+ for (const lineNumber of searchLines) {
295
+ const sourceLine = lines[lineNumber - 1];
296
+ if (sourceLine === void 0 || sourceLine.trim() === "") continue;
297
+ const expression = findLineBraceExpression(sourceLine, lineNumber, column);
298
+ if (expression === void 0) continue;
299
+ const score = (line === void 0 ? 0 : Math.abs(lineNumber - line)) * 10 + expression.distance;
300
+ if (bestMatch === void 0 || score < bestMatch.score) bestMatch = {
301
+ ...expression,
302
+ score
303
+ };
304
+ }
305
+ return bestMatch;
306
+ }
307
+ function getNearbyLineNumbers(line, totalLines, radius) {
308
+ if (line === void 0) return Array.from({ length: totalLines }, (_, index) => index + 1);
309
+ const lineNumbers = [];
310
+ for (let distance = 0; distance <= radius; distance++) {
311
+ const before = line - distance;
312
+ if (before >= 1) lineNumbers.push(before);
313
+ if (distance === 0) continue;
314
+ const after = line + distance;
315
+ if (after <= totalLines) lineNumbers.push(after);
316
+ }
317
+ return lineNumbers;
318
+ }
319
+ function findLineBraceExpression(sourceLine, line, column) {
320
+ let bestMatch;
321
+ for (const match of sourceLine.matchAll(/\{[^{}\n]+\}/gu)) {
322
+ const braceStart = match.index;
323
+ if (braceStart === void 0) continue;
324
+ const expanded = expandBraceExpression(sourceLine, braceStart);
325
+ if (expanded === void 0) continue;
326
+ const distance = column === void 0 ? 0 : Math.abs(braceStart + 1 - column);
327
+ if (bestMatch === void 0 || distance < bestMatch.distance) bestMatch = {
328
+ line,
329
+ column: braceStart + 1,
330
+ sourceLine,
331
+ snippet: expanded,
332
+ distance
333
+ };
334
+ }
335
+ return bestMatch;
336
+ }
337
+ function expandBraceExpression(sourceLine, braceStart) {
338
+ const braceEnd = sourceLine.indexOf("}", braceStart);
339
+ if (braceEnd === -1) return;
340
+ let start = braceStart;
341
+ while (start > 0 && /[A-Za-z0-9_]/u.test(sourceLine[start - 1])) start--;
342
+ let end = braceEnd + 1;
343
+ while (end < sourceLine.length) {
344
+ while (end < sourceLine.length && /[A-Za-z0-9_]/u.test(sourceLine[end])) end++;
345
+ if (sourceLine[end] !== "{") break;
346
+ const chainedEnd = sourceLine.indexOf("}", end);
347
+ if (chainedEnd === -1) break;
348
+ end = chainedEnd + 1;
349
+ }
350
+ return sourceLine.slice(start, end) || void 0;
351
+ }
352
+ function isMdxExpressionError(error) {
353
+ return getErrorField(error, "source") === "micromark-extension-mdx-expression" && getErrorField(error, "ruleId") === "acorn";
354
+ }
355
+ function createDiagnosticSourceContext(originalSourceText, compileSourceText) {
356
+ return {
357
+ originalSourceText,
358
+ lineOffset: getSourceLineOffset(originalSourceText, compileSourceText)
359
+ };
360
+ }
361
+ function getSourceLineOffset(originalSourceText, compileSourceText) {
362
+ const originalLines = originalSourceText.split(/\r?\n/u);
363
+ const compileLines = compileSourceText.split(/\r?\n/u);
364
+ const maxOffset = originalLines.length - compileLines.length;
365
+ for (let offset = 0; offset <= maxOffset; offset++) {
366
+ let isMatch = true;
367
+ for (let index = 0; index < compileLines.length; index++) if (originalLines[offset + index] !== compileLines[index]) {
368
+ isMatch = false;
369
+ break;
370
+ }
371
+ if (isMatch) return offset;
372
+ }
373
+ return maxOffset > 0 ? maxOffset : 0;
374
+ }
197
375
  function getErrorPlace(error) {
198
376
  if (typeof error !== "object" || error === null) return;
199
377
  const candidate = error;
@@ -1 +1 @@
1
- {"version":3,"file":"compiler.mjs","names":["compileMdxJs"],"sources":["../src/compiler.ts"],"sourcesContent":["import {\n compile as compileMdxJs,\n type CompileOptions as MdxJsCompileOptions,\n} from \"@mdx-js/mdx\";\nimport remarkFrontmatterPlugin from \"remark-frontmatter\";\nimport remarkGfmPlugin from \"remark-gfm\";\nimport type { Pluggable, PluggableList, Plugin } from \"unified\";\nimport { VFile } from \"vfile\";\nimport { matter } from \"vfile-matter\";\nimport type { z } from \"zod\";\n\nimport type { HastNode } from \"./types\";\n\nexport { remarkFrontmatterPlugin, remarkGfmPlugin };\n\n/**\n * Represents the source content for an MDX document.\n */\nexport type MdxSource = string | Uint8Array;\n\n/**\n * Options for compiling an MDX document.\n */\nexport interface MdxCompileOptions {\n frontmatterOnly?: boolean | undefined;\n mdxOptions?: MdxJsCompileOptions;\n}\n\nexport interface MdxMessagePoint {\n line?: number | undefined;\n column?: number | undefined;\n offset?: number | undefined;\n}\n\nexport interface MdxMessagePlace {\n start?: MdxMessagePoint | undefined;\n end?: MdxMessagePoint | undefined;\n}\n\nexport interface MdxMessage {\n message: string;\n reason: string;\n source?: string | undefined;\n ruleId?: string | undefined;\n fatal?: boolean | undefined;\n line?: number | undefined;\n column?: number | undefined;\n place?: MdxMessagePlace | undefined;\n sourceLine?: string | undefined;\n snippet?: string | undefined;\n cause?: {\n name?: string | undefined;\n message: string;\n };\n}\n\ntype VFileLikeMessage = VFile[\"messages\"][number];\n\n/**\n * The result of compiling an MDX document.\n * Compiled content is optional, because sometimes we only need the frontmatter.\n */\nexport interface MdxArtifact<TFrontmatter = Record<string, unknown>> {\n compiled?: string | undefined;\n frontmatter: TFrontmatter;\n messages: MdxMessage[];\n}\n\nexport class MdxCompileError extends Error {\n public readonly reason: string;\n public readonly source?: string | undefined;\n public readonly ruleId?: string | undefined;\n public readonly line?: number | undefined;\n public readonly column?: number | undefined;\n public readonly place?: MdxMessagePlace | undefined;\n public readonly sourceLine?: string | undefined;\n public readonly snippet?: string | undefined;\n public readonly diagnostics: MdxMessage[];\n\n public constructor(\n reason: string,\n options: {\n source?: string | undefined;\n ruleId?: string | undefined;\n line?: number | undefined;\n column?: number | undefined;\n place?: MdxMessagePlace | undefined;\n sourceLine?: string | undefined;\n snippet?: string | undefined;\n diagnostics?: MdxMessage[] | undefined;\n cause?: unknown;\n } = {},\n ) {\n super(\n formatDiagnosticMessage(reason, {\n source: options.source,\n ruleId: options.ruleId,\n line: options.line,\n column: options.column,\n sourceLine: options.sourceLine,\n snippet: options.snippet,\n }),\n options.cause === undefined ? undefined : { cause: options.cause },\n );\n\n this.name = \"MdxCompileError\";\n this.reason = reason;\n this.source = options.source;\n this.ruleId = options.ruleId;\n this.line = options.line;\n this.column = options.column;\n this.place = options.place;\n this.sourceLine = options.sourceLine;\n this.snippet = options.snippet;\n this.diagnostics = options.diagnostics ?? [];\n }\n}\n\n/**\n * A reusable compiler for compiling MDX.\n */\nexport class MdxCompiler {\n private readonly mdxOptions: MdxJsCompileOptions;\n private readonly remarkPlugins: PluggableList;\n private readonly rehypePlugins: PluggableList;\n\n public constructor(mdxOptions: MdxJsCompileOptions = {}) {\n this.mdxOptions = mdxOptions;\n this.remarkPlugins = [remarkFrontmatterPlugin, remarkGfmPlugin];\n this.rehypePlugins = [];\n }\n\n public withRemarkPlugin<TOptions>(\n plugin: Plugin<[TOptions?], HastNode, HastNode>,\n options?: TOptions,\n ): MdxCompiler {\n if (options === undefined) {\n this.remarkPlugins.push(plugin as Pluggable);\n } else {\n this.remarkPlugins.push([plugin, options] as Pluggable);\n }\n return this;\n }\n\n public withRehypePlugin<TOptions>(\n plugin: Plugin<[TOptions?], HastNode, HastNode>,\n options?: TOptions,\n ): MdxCompiler {\n if (options === undefined) {\n this.rehypePlugins.push(plugin as Pluggable);\n } else {\n this.rehypePlugins.push([plugin, options] as Pluggable);\n }\n return this;\n }\n\n public async compile<TFrontmatterSchema extends z.ZodSchema>(\n source: MdxSource,\n { frontmatterOnly = false, mdxOptions }: MdxCompileOptions = {},\n frontmatterSchema?: TFrontmatterSchema,\n ): Promise<MdxArtifact<z.output<TFrontmatterSchema>>> {\n const sourceText = toSourceText(source);\n const vfile = new VFile({\n value: source,\n // Needed so vfile doesn't need to access fs.\n cwd: \"/\",\n path: \"/source.mdx\",\n });\n matter(vfile, { strip: true });\n\n const remarkPlugins = [\n ...this.remarkPlugins,\n ...(this.mdxOptions?.remarkPlugins ?? []),\n ...(mdxOptions?.remarkPlugins ?? []),\n ];\n const rehypePlugins = [\n ...this.rehypePlugins,\n ...(this.mdxOptions?.rehypePlugins ?? []),\n ...(mdxOptions?.rehypePlugins ?? []),\n ];\n\n let compiled: string | undefined;\n if (!frontmatterOnly) {\n try {\n const compiledFile = await compileMdxJs(vfile, {\n outputFormat: \"function-body\",\n ...(this.mdxOptions ?? {}),\n ...(mdxOptions ?? {}),\n remarkPlugins,\n rehypePlugins,\n });\n compiled = compiledFile.value.toString();\n } catch (error) {\n throw toMdxCompileError(error, sourceText, vfile.messages);\n }\n }\n\n const frontmatter =\n frontmatterSchema === undefined\n ? (vfile.data.matter ?? {})\n : (frontmatterSchema as z.ZodSchema).parse(vfile.data.matter ?? {});\n\n return {\n compiled,\n frontmatter: frontmatter as z.output<TFrontmatterSchema>,\n messages: normalizeMessages(vfile.messages, sourceText),\n };\n }\n}\n\nfunction toMdxCompileError(\n error: unknown,\n sourceText: string,\n diagnostics: readonly VFileLikeMessage[],\n): MdxCompileError {\n const details = getDiagnosticDetails(sourceText, getErrorPlace(error), error);\n\n return new MdxCompileError(getErrorReason(error), {\n source: getErrorField(error, \"source\"),\n ruleId: getErrorField(error, \"ruleId\"),\n line: details.line,\n column: details.column,\n place: details.place,\n sourceLine: details.sourceLine,\n snippet: details.snippet,\n diagnostics: normalizeMessages(diagnostics, sourceText),\n cause: error,\n });\n}\n\nfunction normalizeMessages(\n messages: readonly VFileLikeMessage[],\n sourceText: string,\n): MdxMessage[] {\n return messages.map((message) => {\n const details = getDiagnosticDetails(\n sourceText,\n toMessagePlace(message),\n message,\n );\n const reason = getErrorReason(message);\n const source = getErrorField(message, \"source\");\n const ruleId = getErrorField(message, \"ruleId\");\n const cause = getErrorCause(message);\n\n return {\n message: formatDiagnosticMessage(reason, {\n source,\n ruleId,\n line: details.line,\n column: details.column,\n sourceLine: details.sourceLine,\n snippet: details.snippet,\n causeMessage: cause?.message,\n }),\n reason,\n source,\n ruleId,\n fatal: typeof message.fatal === \"boolean\" ? message.fatal : undefined,\n line: details.line,\n column: details.column,\n place: details.place,\n sourceLine: details.sourceLine,\n snippet: details.snippet,\n cause,\n };\n });\n}\n\nfunction formatDiagnosticMessage(\n reason: string,\n options: {\n source?: string | undefined;\n ruleId?: string | undefined;\n line?: number | undefined;\n column?: number | undefined;\n sourceLine?: string | undefined;\n snippet?: string | undefined;\n causeMessage?: string | undefined;\n },\n): string {\n const origin =\n options.source === undefined\n ? undefined\n : options.ruleId === undefined\n ? options.source\n : `${options.source}:${options.ruleId}`;\n const location =\n options.line === undefined\n ? undefined\n : options.column === undefined\n ? `at ${options.line}`\n : `at ${options.line}:${options.column}`;\n\n const lines = [[reason, origin, location].filter(Boolean).join(\" \")];\n\n if (options.snippet !== undefined) {\n lines.push(`Source: ${options.snippet}`);\n }\n if (\n options.sourceLine !== undefined &&\n options.sourceLine !== options.snippet\n ) {\n lines.push(`Line: ${options.sourceLine}`);\n }\n if (\n options.causeMessage !== undefined &&\n options.causeMessage !== \"\" &&\n options.causeMessage !== reason\n ) {\n lines.push(`Cause: ${options.causeMessage}`);\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction getDiagnosticDetails(\n sourceText: string,\n place: MdxMessagePlace | undefined,\n error: unknown,\n): {\n line?: number | undefined;\n column?: number | undefined;\n place?: MdxMessagePlace | undefined;\n sourceLine?: string | undefined;\n snippet?: string | undefined;\n} {\n const line = place?.start?.line ?? getErrorNumberField(error, \"line\");\n const column = place?.start?.column ?? getErrorNumberField(error, \"column\");\n const normalizedPlace =\n line === undefined && column === undefined && place === undefined\n ? undefined\n : {\n start: {\n line,\n column,\n offset: place?.start?.offset,\n },\n end:\n place?.end === undefined\n ? undefined\n : {\n line: place.end.line,\n column: place.end.column,\n offset: place.end.offset,\n },\n };\n\n return {\n line,\n column,\n place: normalizedPlace,\n sourceLine: getSourceLine(sourceText, line),\n snippet: getSourceSnippet(sourceText, normalizedPlace),\n };\n}\n\nfunction getSourceLine(\n sourceText: string,\n line: number | undefined,\n): string | undefined {\n if (line === undefined || line < 1) {\n return undefined;\n }\n return sourceText.split(/\\r?\\n/u)[line - 1];\n}\n\nfunction getSourceSnippet(\n sourceText: string,\n place: MdxMessagePlace | undefined,\n): string | undefined {\n if (place?.start?.offset !== undefined && place.end?.offset !== undefined) {\n return sourceText.slice(place.start.offset, place.end.offset) || undefined;\n }\n\n if (place?.start?.line === undefined || place.start.column === undefined) {\n return undefined;\n }\n\n const lines = sourceText.split(/\\r?\\n/u);\n const startLineIndex = place.start.line - 1;\n const endLineIndex = (place.end?.line ?? place.start.line) - 1;\n\n if (startLineIndex < 0 || startLineIndex >= lines.length) {\n return undefined;\n }\n\n const selectedLines = lines.slice(startLineIndex, endLineIndex + 1);\n if (selectedLines.length === 0) {\n return undefined;\n }\n\n selectedLines[0] = selectedLines[0].slice(place.start.column - 1);\n\n const endColumn = place.end?.column;\n if (endColumn !== undefined) {\n const lastIndex = selectedLines.length - 1;\n selectedLines[lastIndex] = selectedLines[lastIndex].slice(0, endColumn - 1);\n }\n\n return selectedLines.join(\"\\n\") || undefined;\n}\n\nfunction getErrorPlace(error: unknown): MdxMessagePlace | undefined {\n if (typeof error !== \"object\" || error === null) {\n return undefined;\n }\n\n const candidate = error as {\n place?: unknown;\n position?: unknown;\n };\n\n return toPlace(candidate.place) ?? toPlace(candidate.position);\n}\n\nfunction toMessagePlace(\n message: VFileLikeMessage,\n): MdxMessagePlace | undefined {\n return toPlace(message.place);\n}\n\nfunction toPlace(value: unknown): MdxMessagePlace | undefined {\n if (typeof value !== \"object\" || value === null) {\n return undefined;\n }\n\n const candidate = value as {\n start?: unknown;\n end?: unknown;\n line?: unknown;\n column?: unknown;\n offset?: unknown;\n };\n\n if (candidate.start !== undefined || candidate.end !== undefined) {\n return {\n start: toPoint(candidate.start),\n end: toPoint(candidate.end),\n };\n }\n\n const point = toPoint(candidate);\n return point === undefined ? undefined : { start: point };\n}\n\nfunction toPoint(value: unknown): MdxMessagePoint | undefined {\n if (typeof value !== \"object\" || value === null) {\n return undefined;\n }\n\n const candidate = value as {\n line?: unknown;\n column?: unknown;\n offset?: unknown;\n };\n const line = typeof candidate.line === \"number\" ? candidate.line : undefined;\n const column =\n typeof candidate.column === \"number\" ? candidate.column : undefined;\n const offset =\n typeof candidate.offset === \"number\" ? candidate.offset : undefined;\n\n if (line === undefined && column === undefined && offset === undefined) {\n return undefined;\n }\n\n return { line, column, offset };\n}\n\nfunction getErrorReason(error: unknown): string {\n if (error instanceof Error && error.message !== \"\") {\n return error.message;\n }\n\n const reason = getErrorField(error, \"reason\");\n if (reason !== undefined && reason !== \"\") {\n return reason;\n }\n\n return String(error);\n}\n\nfunction getErrorCause(\n error: unknown,\n): { name?: string | undefined; message: string } | undefined {\n if (typeof error !== \"object\" || error === null || !(\"cause\" in error)) {\n return undefined;\n }\n\n const cause = (error as { cause?: unknown }).cause;\n if (cause instanceof Error) {\n return {\n name: cause.name,\n message: cause.message,\n };\n }\n if (typeof cause === \"string\" && cause !== \"\") {\n return {\n message: cause,\n };\n }\n\n return undefined;\n}\n\nfunction getErrorField(\n error: unknown,\n field: \"source\" | \"ruleId\" | \"reason\",\n): string | undefined {\n if (typeof error !== \"object\" || error === null || !(field in error)) {\n return undefined;\n }\n\n const value = (error as Record<string, unknown>)[field];\n return typeof value === \"string\" && value !== \"\" ? value : undefined;\n}\n\nfunction getErrorNumberField(\n error: unknown,\n field: \"line\" | \"column\",\n): number | undefined {\n if (typeof error !== \"object\" || error === null || !(field in error)) {\n return undefined;\n }\n\n const value = (error as Record<string, unknown>)[field];\n return typeof value === \"number\" ? value : undefined;\n}\n\nfunction toSourceText(source: MdxSource): string {\n return typeof source === \"string\" ? source : new TextDecoder().decode(source);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"compiler.mjs","names":["compileMdxJs"],"sources":["../src/compiler.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { Pluggable, PluggableList, Plugin } from \"unified\";\n\nimport { compile as compileMdxJs, type CompileOptions as MdxJsCompileOptions } from \"@mdx-js/mdx\";\nimport { validateStandardSchema } from \"@temelj/standard-schema\";\nimport remarkFrontmatterPlugin from \"remark-frontmatter\";\nimport remarkGfmPlugin from \"remark-gfm\";\nimport { VFile } from \"vfile\";\nimport { matter } from \"vfile-matter\";\n\nimport type { HastNode } from \"./types\";\n\nexport { remarkFrontmatterPlugin, remarkGfmPlugin };\n\n/**\n * Represents the source content for an MDX document.\n */\nexport type MdxSource = string | Uint8Array;\n\n/**\n * Options for compiling an MDX document.\n */\nexport interface MdxCompileOptions {\n frontmatterOnly?: boolean | undefined;\n mdxOptions?: MdxJsCompileOptions;\n}\n\nexport interface MdxMessagePoint {\n line?: number | undefined;\n column?: number | undefined;\n offset?: number | undefined;\n}\n\nexport interface MdxMessagePlace {\n start?: MdxMessagePoint | undefined;\n end?: MdxMessagePoint | undefined;\n}\n\nexport interface MdxMessage {\n message: string;\n reason: string;\n source?: string | undefined;\n ruleId?: string | undefined;\n fatal?: boolean | undefined;\n line?: number | undefined;\n column?: number | undefined;\n place?: MdxMessagePlace | undefined;\n sourceLine?: string | undefined;\n snippet?: string | undefined;\n sourcePointer?: string | undefined;\n hint?: string | undefined;\n cause?: {\n name?: string | undefined;\n message: string;\n };\n}\n\ntype VFileLikeMessage = VFile[\"messages\"][number];\n\ninterface MdxDiagnosticSourceContext {\n originalSourceText: string;\n lineOffset: number;\n}\n\n/**\n * The result of compiling an MDX document.\n * Compiled content is optional, because sometimes we only need the frontmatter.\n */\nexport interface MdxArtifact<TFrontmatter = Record<string, unknown>> {\n compiled?: string | undefined;\n frontmatter: TFrontmatter;\n messages: MdxMessage[];\n}\n\nexport class MdxCompileError extends Error {\n public readonly reason: string;\n public readonly source?: string | undefined;\n public readonly ruleId?: string | undefined;\n public readonly line?: number | undefined;\n public readonly column?: number | undefined;\n public readonly place?: MdxMessagePlace | undefined;\n public readonly sourceLine?: string | undefined;\n public readonly snippet?: string | undefined;\n public readonly sourcePointer?: string | undefined;\n public readonly hint?: string | undefined;\n public readonly diagnostics: MdxMessage[];\n\n public constructor(\n reason: string,\n options: {\n source?: string | undefined;\n ruleId?: string | undefined;\n line?: number | undefined;\n column?: number | undefined;\n place?: MdxMessagePlace | undefined;\n sourceLine?: string | undefined;\n snippet?: string | undefined;\n sourcePointer?: string | undefined;\n hint?: string | undefined;\n causeMessage?: string | undefined;\n diagnostics?: MdxMessage[] | undefined;\n cause?: unknown;\n } = {},\n ) {\n super(\n formatDiagnosticMessage(reason, {\n source: options.source,\n ruleId: options.ruleId,\n line: options.line,\n column: options.column,\n sourceLine: options.sourceLine,\n snippet: options.snippet,\n sourcePointer: options.sourcePointer,\n hint: options.hint,\n causeMessage: options.causeMessage,\n }),\n options.cause === undefined ? undefined : { cause: options.cause },\n );\n\n this.name = \"MdxCompileError\";\n this.reason = reason;\n this.source = options.source;\n this.ruleId = options.ruleId;\n this.line = options.line;\n this.column = options.column;\n this.place = options.place;\n this.sourceLine = options.sourceLine;\n this.snippet = options.snippet;\n this.sourcePointer = options.sourcePointer;\n this.hint = options.hint;\n this.diagnostics = options.diagnostics ?? [];\n }\n}\n\n/**\n * A reusable compiler for compiling MDX.\n */\nexport class MdxCompiler {\n private readonly mdxOptions: MdxJsCompileOptions;\n private readonly remarkPlugins: PluggableList;\n private readonly rehypePlugins: PluggableList;\n\n public constructor(mdxOptions: MdxJsCompileOptions = {}) {\n this.mdxOptions = mdxOptions;\n this.remarkPlugins = [remarkFrontmatterPlugin, remarkGfmPlugin];\n this.rehypePlugins = [];\n }\n\n public withRemarkPlugin<TOptions>(\n plugin: Plugin<[TOptions?], HastNode, HastNode>,\n options?: TOptions,\n ): MdxCompiler {\n if (options === undefined) {\n this.remarkPlugins.push(plugin as Pluggable);\n } else {\n this.remarkPlugins.push([plugin, options] as Pluggable);\n }\n return this;\n }\n\n public withRehypePlugin<TOptions>(\n plugin: Plugin<[TOptions?], HastNode, HastNode>,\n options?: TOptions,\n ): MdxCompiler {\n if (options === undefined) {\n this.rehypePlugins.push(plugin as Pluggable);\n } else {\n this.rehypePlugins.push([plugin, options] as Pluggable);\n }\n return this;\n }\n\n public async compile(\n source: MdxSource,\n options?: MdxCompileOptions,\n ): Promise<MdxArtifact<Record<string, unknown>>>;\n public async compile<TFrontmatterSchema extends StandardSchemaV1>(\n source: MdxSource,\n options: MdxCompileOptions | undefined,\n frontmatterSchema: TFrontmatterSchema,\n ): Promise<MdxArtifact<StandardSchemaV1.InferOutput<TFrontmatterSchema>>>;\n public async compile<TFrontmatterSchema extends StandardSchemaV1>(\n source: MdxSource,\n { frontmatterOnly = false, mdxOptions }: MdxCompileOptions = {},\n frontmatterSchema?: TFrontmatterSchema,\n ): Promise<\n MdxArtifact<\n TFrontmatterSchema extends StandardSchemaV1\n ? StandardSchemaV1.InferOutput<TFrontmatterSchema>\n : Record<string, unknown>\n >\n > {\n const sourceText = toSourceText(source);\n const vfile = new VFile({\n value: source,\n // Needed so vfile doesn't need to access fs.\n cwd: \"/\",\n path: \"/source.mdx\",\n });\n matter(vfile, { strip: true });\n const compileSourceText = toSourceText(vfile.value as MdxSource);\n const sourceContext = createDiagnosticSourceContext(sourceText, compileSourceText);\n\n const remarkPlugins = [\n ...this.remarkPlugins,\n ...(this.mdxOptions?.remarkPlugins ?? []),\n ...(mdxOptions?.remarkPlugins ?? []),\n ];\n const rehypePlugins = [\n ...this.rehypePlugins,\n ...(this.mdxOptions?.rehypePlugins ?? []),\n ...(mdxOptions?.rehypePlugins ?? []),\n ];\n\n let compiled: string | undefined;\n if (!frontmatterOnly) {\n try {\n const compiledFile = await compileMdxJs(vfile, {\n outputFormat: \"function-body\",\n ...this.mdxOptions,\n ...mdxOptions,\n remarkPlugins,\n rehypePlugins,\n });\n compiled = compiledFile.value.toString();\n } catch (error) {\n throw toMdxCompileError(error, sourceContext, vfile.messages);\n }\n }\n\n const frontmatter =\n frontmatterSchema === undefined\n ? (vfile.data.matter ?? {})\n : await validateStandardSchema(\n frontmatterSchema,\n vfile.data.matter ?? {},\n \"Frontmatter validation failed\",\n );\n\n return {\n compiled,\n frontmatter: frontmatter as TFrontmatterSchema extends StandardSchemaV1\n ? StandardSchemaV1.InferOutput<TFrontmatterSchema>\n : Record<string, unknown>,\n messages: normalizeMessages(vfile.messages, sourceContext),\n };\n }\n}\n\nfunction toMdxCompileError(\n error: unknown,\n sourceContext: MdxDiagnosticSourceContext,\n diagnostics: readonly VFileLikeMessage[],\n): MdxCompileError {\n const details = getDiagnosticDetails(sourceContext, getErrorPlace(error), error);\n const cause = getErrorCause(error);\n\n return new MdxCompileError(getErrorReason(error), {\n source: getErrorField(error, \"source\"),\n ruleId: getErrorField(error, \"ruleId\"),\n line: details.line,\n column: details.column,\n place: details.place,\n sourceLine: details.sourceLine,\n snippet: details.snippet,\n sourcePointer: details.sourcePointer,\n hint: details.hint,\n causeMessage: cause?.message,\n diagnostics: normalizeMessages(diagnostics, sourceContext),\n cause: error,\n });\n}\n\nfunction normalizeMessages(\n messages: readonly VFileLikeMessage[],\n sourceContext: MdxDiagnosticSourceContext,\n): MdxMessage[] {\n return messages.map((message) => {\n const details = getDiagnosticDetails(sourceContext, toMessagePlace(message), message);\n const reason = getErrorReason(message);\n const source = getErrorField(message, \"source\");\n const ruleId = getErrorField(message, \"ruleId\");\n const cause = getErrorCause(message);\n\n return {\n message: formatDiagnosticMessage(reason, {\n source,\n ruleId,\n line: details.line,\n column: details.column,\n sourceLine: details.sourceLine,\n snippet: details.snippet,\n sourcePointer: details.sourcePointer,\n hint: details.hint,\n causeMessage: cause?.message,\n }),\n reason,\n source,\n ruleId,\n fatal: typeof message.fatal === \"boolean\" ? message.fatal : undefined,\n line: details.line,\n column: details.column,\n place: details.place,\n sourceLine: details.sourceLine,\n snippet: details.snippet,\n sourcePointer: details.sourcePointer,\n hint: details.hint,\n cause,\n };\n });\n}\n\nfunction formatDiagnosticMessage(\n reason: string,\n options: {\n source?: string | undefined;\n ruleId?: string | undefined;\n line?: number | undefined;\n column?: number | undefined;\n sourceLine?: string | undefined;\n snippet?: string | undefined;\n sourcePointer?: string | undefined;\n hint?: string | undefined;\n causeMessage?: string | undefined;\n },\n): string {\n const origin =\n options.source === undefined\n ? undefined\n : options.ruleId === undefined\n ? options.source\n : `${options.source}:${options.ruleId}`;\n const location =\n options.line === undefined\n ? undefined\n : options.column === undefined\n ? `at ${options.line}`\n : `at ${options.line}:${options.column}`;\n\n const lines = [[reason, origin, location].filter(Boolean).join(\" \")];\n\n if (options.snippet !== undefined) {\n lines.push(`Source: ${options.snippet}`);\n }\n if (options.sourceLine !== undefined && options.sourceLine !== options.snippet) {\n lines.push(`Line: ${options.sourceLine}`);\n if (options.sourcePointer !== undefined) {\n lines.push(` ${options.sourcePointer}`);\n }\n }\n if (\n options.causeMessage !== undefined &&\n options.causeMessage !== \"\" &&\n options.causeMessage !== reason\n ) {\n lines.push(`Cause: ${options.causeMessage}`);\n }\n if (options.hint !== undefined) {\n lines.push(`Hint: ${options.hint}`);\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction getDiagnosticDetails(\n sourceContext: MdxDiagnosticSourceContext,\n place: MdxMessagePlace | undefined,\n error: unknown,\n): {\n line?: number | undefined;\n column?: number | undefined;\n place?: MdxMessagePlace | undefined;\n sourceLine?: string | undefined;\n snippet?: string | undefined;\n sourcePointer?: string | undefined;\n hint?: string | undefined;\n} {\n const compileLine = place?.start?.line ?? getErrorNumberField(error, \"line\");\n let line = compileLine === undefined ? undefined : compileLine + sourceContext.lineOffset;\n let column = place?.start?.column ?? getErrorNumberField(error, \"column\");\n let normalizedPlace = mapPlaceToOriginalSource(\n place === undefined && compileLine === undefined && column === undefined\n ? undefined\n : {\n start: {\n line: compileLine,\n column,\n offset: place?.start?.offset,\n },\n end:\n place?.end === undefined\n ? undefined\n : {\n line: place.end.line,\n column: place.end.column,\n offset: place.end.offset,\n },\n },\n sourceContext.lineOffset,\n );\n let sourceLine = getSourceLine(sourceContext.originalSourceText, line);\n let snippet = getSourceSnippet(sourceContext.originalSourceText, normalizedPlace);\n let sourcePointer = getSourcePointer(column);\n let hint: string | undefined;\n\n const refinedExpressionContext = getMdxExpressionContext(sourceContext.originalSourceText, {\n line,\n column,\n sourceLine,\n snippet,\n error,\n });\n\n if (refinedExpressionContext !== undefined) {\n line = refinedExpressionContext.line;\n column = refinedExpressionContext.column;\n sourceLine = refinedExpressionContext.sourceLine;\n snippet = refinedExpressionContext.snippet;\n sourcePointer = refinedExpressionContext.sourcePointer;\n hint = refinedExpressionContext.hint;\n normalizedPlace = {\n start: {\n line,\n column,\n offset: undefined,\n },\n end: undefined,\n };\n }\n\n return {\n line,\n column,\n place: normalizedPlace,\n sourceLine,\n snippet,\n sourcePointer,\n hint,\n };\n}\n\nfunction getMdxExpressionContext(\n sourceText: string,\n options: {\n line?: number | undefined;\n column?: number | undefined;\n sourceLine?: string | undefined;\n snippet?: string | undefined;\n error: unknown;\n },\n):\n | {\n line: number;\n column: number;\n sourceLine: string;\n snippet: string;\n sourcePointer: string;\n hint?: string | undefined;\n }\n | undefined {\n if (!shouldRefineMdxExpressionContext(options)) {\n return undefined;\n }\n\n const candidate = findSuspiciousBraceExpression(sourceText, options.line, options.column);\n if (candidate === undefined) {\n return undefined;\n }\n\n return {\n line: candidate.line,\n column: candidate.column,\n sourceLine: candidate.sourceLine,\n snippet: candidate.snippet,\n sourcePointer: getSourcePointer(candidate.column) ?? \"^\",\n hint:\n getErrorCause(options.error)?.message === \"Unexpected content after expression\"\n ? \"MDX treats `{...}` as JavaScript. If you meant literal braces in text, escape them or wrap the text in code.\"\n : undefined,\n };\n}\n\nfunction shouldRefineMdxExpressionContext(options: {\n sourceLine?: string | undefined;\n snippet?: string | undefined;\n error: unknown;\n}): boolean {\n const trimmedSourceLine = options.sourceLine?.trim();\n\n return (\n isMdxExpressionError(options.error) &&\n getErrorCause(options.error)?.message === \"Unexpected content after expression\" &&\n (options.sourceLine === undefined ||\n options.sourceLine.trim() === \"\" ||\n options.snippet === undefined) &&\n !(trimmedSourceLine?.startsWith(\"{\") && trimmedSourceLine.endsWith(\"}\"))\n );\n}\n\nfunction getSourceLine(sourceText: string, line: number | undefined): string | undefined {\n if (line === undefined || line < 1) {\n return undefined;\n }\n return sourceText.split(/\\r?\\n/u)[line - 1];\n}\n\nfunction getSourceSnippet(\n sourceText: string,\n place: MdxMessagePlace | undefined,\n): string | undefined {\n if (place?.start?.offset !== undefined && place.end?.offset !== undefined) {\n return sourceText.slice(place.start.offset, place.end.offset) || undefined;\n }\n\n if (place?.start?.line === undefined || place.start.column === undefined) {\n return undefined;\n }\n\n if (place.end === undefined) {\n const lines = sourceText.split(/\\r?\\n/u);\n const line = lines[place.start.line - 1];\n if (line === undefined) {\n return undefined;\n }\n\n const character = line.at(place.start.column - 1);\n return character === undefined || character.trim() === \"\" ? undefined : character;\n }\n\n const lines = sourceText.split(/\\r?\\n/u);\n const startLineIndex = place.start.line - 1;\n const endLineIndex = (place.end?.line ?? place.start.line) - 1;\n\n if (startLineIndex < 0 || startLineIndex >= lines.length) {\n return undefined;\n }\n\n const selectedLines = lines.slice(startLineIndex, endLineIndex + 1);\n if (selectedLines.length === 0) {\n return undefined;\n }\n\n selectedLines[0] = selectedLines[0].slice(place.start.column - 1);\n\n const endColumn = place.end?.column;\n if (endColumn !== undefined) {\n const lastIndex = selectedLines.length - 1;\n selectedLines[lastIndex] = selectedLines[lastIndex].slice(0, endColumn - 1);\n }\n\n return selectedLines.join(\"\\n\") || undefined;\n}\n\nfunction getSourcePointer(column: number | undefined): string | undefined {\n if (column === undefined || column < 1) {\n return undefined;\n }\n\n return `${\" \".repeat(column - 1)}^`;\n}\n\nfunction mapPlaceToOriginalSource(\n place: MdxMessagePlace | undefined,\n lineOffset: number,\n): MdxMessagePlace | undefined {\n if (place === undefined) {\n return undefined;\n }\n\n return {\n start: mapPointToOriginalSource(place.start, lineOffset),\n end: mapPointToOriginalSource(place.end, lineOffset),\n };\n}\n\nfunction mapPointToOriginalSource(\n point: MdxMessagePoint | undefined,\n lineOffset: number,\n): MdxMessagePoint | undefined {\n if (point === undefined) {\n return undefined;\n }\n\n return {\n line: point.line === undefined ? undefined : point.line + lineOffset,\n column: point.column,\n offset: lineOffset === 0 ? point.offset : undefined,\n };\n}\n\nfunction findSuspiciousBraceExpression(\n sourceText: string,\n line: number | undefined,\n column: number | undefined,\n):\n | {\n line: number;\n column: number;\n sourceLine: string;\n snippet: string;\n }\n | undefined {\n const lines = sourceText.split(/\\r?\\n/u);\n const searchLines = getNearbyLineNumbers(line, lines.length, 2);\n let bestMatch:\n | {\n line: number;\n column: number;\n sourceLine: string;\n snippet: string;\n score: number;\n }\n | undefined;\n\n for (const lineNumber of searchLines) {\n const sourceLine = lines[lineNumber - 1];\n if (sourceLine === undefined || sourceLine.trim() === \"\") {\n continue;\n }\n\n const expression = findLineBraceExpression(sourceLine, lineNumber, column);\n if (expression === undefined) {\n continue;\n }\n\n const lineDistance = line === undefined ? 0 : Math.abs(lineNumber - line);\n const score = lineDistance * 10 + expression.distance;\n\n if (bestMatch === undefined || score < bestMatch.score) {\n bestMatch = {\n ...expression,\n score,\n };\n }\n }\n\n return bestMatch;\n}\n\nfunction getNearbyLineNumbers(\n line: number | undefined,\n totalLines: number,\n radius: number,\n): number[] {\n if (line === undefined) {\n return Array.from({ length: totalLines }, (_, index) => index + 1);\n }\n\n const lineNumbers: number[] = [];\n for (let distance = 0; distance <= radius; distance++) {\n const before = line - distance;\n if (before >= 1) {\n lineNumbers.push(before);\n }\n\n if (distance === 0) {\n continue;\n }\n\n const after = line + distance;\n if (after <= totalLines) {\n lineNumbers.push(after);\n }\n }\n\n return lineNumbers;\n}\n\nfunction findLineBraceExpression(\n sourceLine: string,\n line: number,\n column: number | undefined,\n):\n | {\n line: number;\n column: number;\n sourceLine: string;\n snippet: string;\n distance: number;\n }\n | undefined {\n let bestMatch:\n | {\n line: number;\n column: number;\n sourceLine: string;\n snippet: string;\n distance: number;\n }\n | undefined;\n\n for (const match of sourceLine.matchAll(/\\{[^{}\\n]+\\}/gu)) {\n const braceStart = match.index;\n if (braceStart === undefined) {\n continue;\n }\n\n const expanded = expandBraceExpression(sourceLine, braceStart);\n if (expanded === undefined) {\n continue;\n }\n\n const distance = column === undefined ? 0 : Math.abs(braceStart + 1 - column);\n if (bestMatch === undefined || distance < bestMatch.distance) {\n bestMatch = {\n line,\n column: braceStart + 1,\n sourceLine,\n snippet: expanded,\n distance,\n };\n }\n }\n\n return bestMatch;\n}\n\nfunction expandBraceExpression(sourceLine: string, braceStart: number): string | undefined {\n const braceEnd = sourceLine.indexOf(\"}\", braceStart);\n if (braceEnd === -1) {\n return undefined;\n }\n\n let start = braceStart;\n while (start > 0 && /[A-Za-z0-9_]/u.test(sourceLine[start - 1])) {\n start--;\n }\n\n let end = braceEnd + 1;\n while (end < sourceLine.length) {\n while (end < sourceLine.length && /[A-Za-z0-9_]/u.test(sourceLine[end])) {\n end++;\n }\n\n if (sourceLine[end] !== \"{\") {\n break;\n }\n\n const chainedEnd = sourceLine.indexOf(\"}\", end);\n if (chainedEnd === -1) {\n break;\n }\n end = chainedEnd + 1;\n }\n\n return sourceLine.slice(start, end) || undefined;\n}\n\nfunction isMdxExpressionError(error: unknown): boolean {\n return (\n getErrorField(error, \"source\") === \"micromark-extension-mdx-expression\" &&\n getErrorField(error, \"ruleId\") === \"acorn\"\n );\n}\n\nfunction createDiagnosticSourceContext(\n originalSourceText: string,\n compileSourceText: string,\n): MdxDiagnosticSourceContext {\n return {\n originalSourceText,\n lineOffset: getSourceLineOffset(originalSourceText, compileSourceText),\n };\n}\n\nfunction getSourceLineOffset(originalSourceText: string, compileSourceText: string): number {\n const originalLines = originalSourceText.split(/\\r?\\n/u);\n const compileLines = compileSourceText.split(/\\r?\\n/u);\n const maxOffset = originalLines.length - compileLines.length;\n\n for (let offset = 0; offset <= maxOffset; offset++) {\n let isMatch = true;\n for (let index = 0; index < compileLines.length; index++) {\n if (originalLines[offset + index] !== compileLines[index]) {\n isMatch = false;\n break;\n }\n }\n\n if (isMatch) {\n return offset;\n }\n }\n\n return maxOffset > 0 ? maxOffset : 0;\n}\n\nfunction getErrorPlace(error: unknown): MdxMessagePlace | undefined {\n if (typeof error !== \"object\" || error === null) {\n return undefined;\n }\n\n const candidate = error as {\n place?: unknown;\n position?: unknown;\n };\n\n return toPlace(candidate.place) ?? toPlace(candidate.position);\n}\n\nfunction toMessagePlace(message: VFileLikeMessage): MdxMessagePlace | undefined {\n return toPlace(message.place);\n}\n\nfunction toPlace(value: unknown): MdxMessagePlace | undefined {\n if (typeof value !== \"object\" || value === null) {\n return undefined;\n }\n\n const candidate = value as {\n start?: unknown;\n end?: unknown;\n line?: unknown;\n column?: unknown;\n offset?: unknown;\n };\n\n if (candidate.start !== undefined || candidate.end !== undefined) {\n return {\n start: toPoint(candidate.start),\n end: toPoint(candidate.end),\n };\n }\n\n const point = toPoint(candidate);\n return point === undefined ? undefined : { start: point };\n}\n\nfunction toPoint(value: unknown): MdxMessagePoint | undefined {\n if (typeof value !== \"object\" || value === null) {\n return undefined;\n }\n\n const candidate = value as {\n line?: unknown;\n column?: unknown;\n offset?: unknown;\n };\n const line = typeof candidate.line === \"number\" ? candidate.line : undefined;\n const column = typeof candidate.column === \"number\" ? candidate.column : undefined;\n const offset = typeof candidate.offset === \"number\" ? candidate.offset : undefined;\n\n if (line === undefined && column === undefined && offset === undefined) {\n return undefined;\n }\n\n return { line, column, offset };\n}\n\nfunction getErrorReason(error: unknown): string {\n if (error instanceof Error && error.message !== \"\") {\n return error.message;\n }\n\n const reason = getErrorField(error, \"reason\");\n if (reason !== undefined && reason !== \"\") {\n return reason;\n }\n\n return String(error);\n}\n\nfunction getErrorCause(error: unknown): { name?: string | undefined; message: string } | undefined {\n if (typeof error !== \"object\" || error === null || !(\"cause\" in error)) {\n return undefined;\n }\n\n const cause = (error as { cause?: unknown }).cause;\n if (cause instanceof Error) {\n return {\n name: cause.name,\n message: cause.message,\n };\n }\n if (typeof cause === \"string\" && cause !== \"\") {\n return {\n message: cause,\n };\n }\n\n return undefined;\n}\n\nfunction getErrorField(error: unknown, field: \"source\" | \"ruleId\" | \"reason\"): string | undefined {\n if (typeof error !== \"object\" || error === null || !(field in error)) {\n return undefined;\n }\n\n const value = (error as Record<string, unknown>)[field];\n return typeof value === \"string\" && value !== \"\" ? value : undefined;\n}\n\nfunction getErrorNumberField(error: unknown, field: \"line\" | \"column\"): number | undefined {\n if (typeof error !== \"object\" || error === null || !(field in error)) {\n return undefined;\n }\n\n const value = (error as Record<string, unknown>)[field];\n return typeof value === \"number\" ? value : undefined;\n}\n\nfunction toSourceText(source: MdxSource): string {\n return typeof source === \"string\" ? source : new TextDecoder().decode(source);\n}\n"],"mappings":"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"}
package/dist/mod.d.mts CHANGED
@@ -4,4 +4,4 @@ import { HeadingIdPluginOptions, headingIdPlugin } from "./plugins/heading-id/pl
4
4
  import { removeImportsExportsPlugin } from "./plugins/remove-imports-exports.mjs";
5
5
  import { SyntaxHighlightPluginOptions, syntaxHighlightPlugin } from "./plugins/syntax-highlight/plugin.mjs";
6
6
  import { MdxTreeProcessor, treeProcessorPlugin } from "./plugins/tree-processor/plugin.mjs";
7
- export { Element as HastElement, Node as HastNode, HeadingIdPluginOptions, MdxArtifact, MdxCompileError, MdxCompileOptions, MdxCompiler, MdxMessage, MdxMessagePlace, MdxMessagePoint, MdxSource, MdxTreeProcessor, SyntaxHighlightPluginOptions, headingIdPlugin, remarkFrontmatterPlugin, remarkGfmPlugin, removeImportsExportsPlugin, syntaxHighlightPlugin, treeProcessorPlugin };
7
+ export { type Element as HastElement, type Node as HastNode, HeadingIdPluginOptions, MdxArtifact, MdxCompileError, MdxCompileOptions, MdxCompiler, MdxMessage, MdxMessagePlace, MdxMessagePoint, MdxSource, MdxTreeProcessor, SyntaxHighlightPluginOptions, headingIdPlugin, remarkFrontmatterPlugin, remarkGfmPlugin, removeImportsExportsPlugin, syntaxHighlightPlugin, treeProcessorPlugin };
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.mts","names":[],"sources":["../../../src/plugins/heading-id/plugin.ts"],"mappings":";;;;;;AAUA;UAAiB,sBAAA;;EAEf,MAAA;AAAA;AAMF;;;AAAA,cAAa,eAAA,EAAiB,MAAA,EAC3B,sBAAA,IACD,IAAA,EACA,IAAA"}
1
+ {"version":3,"file":"plugin.d.mts","names":[],"sources":["../../../src/plugins/heading-id/plugin.ts"],"mappings":";;;;;;AAWA;UAAiB,sBAAA;;EAEf,MAAM;AAAA;AAMR;;;AAAA,cAAa,eAAA,EAAiB,MAAA,EAAQ,sBAAA,IAA0B,IAAA,EAAU,IAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.mjs","names":["hastToString"],"sources":["../../../src/plugins/heading-id/plugin.ts"],"sourcesContent":["import GithubSlugger from \"github-slugger\";\nimport { toString as hastToString } from \"hast-util-to-string\";\nimport type { Plugin } from \"unified\";\nimport { visit } from \"unist-util-visit\";\n\nimport type { HastElement, HastNode } from \"../../types\";\n\n/**\n * Options for {@linkcode headingIdPlugin}.\n */\nexport interface HeadingIdPluginOptions {\n /** Prefix for title slugs. */\n prefix?: string;\n}\n\n/**\n * A rehype plugin that adds IDs to heading tags.\n */\nexport const headingIdPlugin: Plugin<\n [HeadingIdPluginOptions?],\n HastNode,\n HastNode\n> = ({ prefix } = {}) => {\n const slugger = new GithubSlugger();\n return (tree) => {\n slugger.reset();\n visit(\n tree,\n \"element\",\n function visitor(\n node: HastElement,\n index: number,\n parent: HastElement,\n ): void {\n if (!parent || index === null || !node.tagName.startsWith(\"h\")) return;\n const id = (prefix ?? \"\") + slugger.slug(hastToString(node));\n node.properties.id = id;\n },\n );\n return tree;\n };\n};\n"],"mappings":";;;;;;;AAkBA,MAAa,mBAIR,EAAE,WAAW,EAAE,KAAK;CACvB,MAAM,UAAU,IAAI,eAAe;AACnC,SAAQ,SAAS;AACf,UAAQ,OAAO;AACf,QACE,MACA,WACA,SAAS,QACP,MACA,OACA,QACM;AACN,OAAI,CAAC,UAAU,UAAU,QAAQ,CAAC,KAAK,QAAQ,WAAW,IAAI,CAAE;GAChE,MAAM,MAAM,UAAU,MAAM,QAAQ,KAAKA,SAAa,KAAK,CAAC;AAC5D,QAAK,WAAW,KAAK;IAExB;AACD,SAAO"}
1
+ {"version":3,"file":"plugin.mjs","names":["hastToString"],"sources":["../../../src/plugins/heading-id/plugin.ts"],"sourcesContent":["import type { Plugin } from \"unified\";\n\nimport GithubSlugger from \"github-slugger\";\nimport { toString as hastToString } from \"hast-util-to-string\";\nimport { visit } from \"unist-util-visit\";\n\nimport type { HastElement, HastNode } from \"../../types\";\n\n/**\n * Options for {@linkcode headingIdPlugin}.\n */\nexport interface HeadingIdPluginOptions {\n /** Prefix for title slugs. */\n prefix?: string;\n}\n\n/**\n * A rehype plugin that adds IDs to heading tags.\n */\nexport const headingIdPlugin: Plugin<[HeadingIdPluginOptions?], HastNode, HastNode> = ({\n prefix,\n} = {}) => {\n const slugger = new GithubSlugger();\n return (tree) => {\n slugger.reset();\n visit(\n tree,\n \"element\",\n function visitor(node: HastElement, index: number, parent: HastElement): void {\n if (!parent || index === null || !node.tagName.startsWith(\"h\")) return;\n const id = (prefix ?? \"\") + slugger.slug(hastToString(node));\n node.properties.id = id;\n },\n );\n return tree;\n };\n};\n"],"mappings":";;;;;;;AAmBA,MAAa,mBAA0E,EACrF,WACE,CAAC,MAAM;CACT,MAAM,UAAU,IAAI,cAAc;CAClC,QAAQ,SAAS;EACf,QAAQ,MAAM;EACd,MACE,MACA,WACA,SAAS,QAAQ,MAAmB,OAAe,QAA2B;GAC5E,IAAI,CAAC,UAAU,UAAU,QAAQ,CAAC,KAAK,QAAQ,WAAW,GAAG,GAAG;GAChE,MAAM,MAAM,UAAU,MAAM,QAAQ,KAAKA,SAAa,IAAI,CAAC;GAC3D,KAAK,WAAW,KAAK;EACvB,CACF;EACA,OAAO;CACT;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"remove-imports-exports.d.mts","names":[],"sources":["../../src/plugins/remove-imports-exports.ts"],"mappings":";;;;;;AAQA;cAAa,0BAAA,EAA4B,MAAA,YAEvC,IAAA,EACA,IAAA"}
1
+ {"version":3,"file":"remove-imports-exports.d.mts","names":[],"sources":["../../src/plugins/remove-imports-exports.ts"],"mappings":";;;;;;AASA;cAAa,0BAAA,EAA4B,MAAA,YAAkB,IAAA,EAAU,IAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"remove-imports-exports.mjs","names":[],"sources":["../../src/plugins/remove-imports-exports.ts"],"sourcesContent":["import type { Plugin } from \"unified\";\nimport { remove } from \"unist-util-remove\";\n\nimport type { HastNode } from \"../types\";\n\n/**\n * A remark plugin which removes all import and export statements.\n */\nexport const removeImportsExportsPlugin: Plugin<\n [unknown],\n HastNode,\n HastNode\n> = () => {\n return (tree: HastNode) => {\n remove(tree, \"mdxjsEsm\");\n return tree;\n };\n};\n"],"mappings":";;;;;AAQA,MAAa,mCAIH;AACR,SAAQ,SAAmB;AACzB,SAAO,MAAM,WAAW;AACxB,SAAO"}
1
+ {"version":3,"file":"remove-imports-exports.mjs","names":[],"sources":["../../src/plugins/remove-imports-exports.ts"],"sourcesContent":["import type { Plugin } from \"unified\";\n\nimport { remove } from \"unist-util-remove\";\n\nimport type { HastNode } from \"../types\";\n\n/**\n * A remark plugin which removes all import and export statements.\n */\nexport const removeImportsExportsPlugin: Plugin<[unknown], HastNode, HastNode> = () => {\n return (tree: HastNode) => {\n remove(tree, \"mdxjsEsm\");\n return tree;\n };\n};\n"],"mappings":";;;;;AASA,MAAa,mCAA0E;CACrF,QAAQ,SAAmB;EACzB,OAAO,MAAM,UAAU;EACvB,OAAO;CACT;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"code-meta.d.mts","names":[],"sources":["../../../src/plugins/syntax-highlight/code-meta.ts"],"mappings":";;;;UAKiB,WAAA;EACf,SAAA,GAAY,wBAAA;EACZ,eAAA,GAAkB,sBAAA;EAClB,WAAA,GAAc,sBAAA;EACd,QAAA;AAAA;AAAA,KAGU,wBAAA;EACV,SAAA,EAAW,YAAA;AAAA;AAAA,KAGD,sBAAA;EACV,SAAA,EAAW,YAAA;AAAA;AAAA,KAGD,sBAAA;EACV,YAAA,EAAc,YAAA;AAAA;AAAA,iBAGA,eAAA,CAAgB,IAAA,EAAM,OAAA,GAAc,WAAA"}
1
+ {"version":3,"file":"code-meta.d.mts","names":[],"sources":["../../../src/plugins/syntax-highlight/code-meta.ts"],"mappings":";;;;UAKiB,WAAA;EACf,SAAA,GAAY,wBAAA;EACZ,eAAA,GAAkB,sBAAA;EAClB,WAAA,GAAc,sBAAA;EACd,QAAA;AAAA;AAAA,KAGU,wBAAA;EACV,SAAA,EAAW,YAAY;AAAA;AAAA,KAGb,sBAAA;EACV,SAAA,EAAW,YAAY;AAAA;AAAA,KAGb,sBAAA;EACV,YAAA,EAAc,YAAY;AAAA;AAAA,iBAGZ,eAAA,CAAgB,IAAA,EAAM,OAAA,GAAc,WAAW"}
@@ -1,15 +1,16 @@
1
+ import { ss, validateStandardSchemaSync } from "@temelj/standard-schema";
1
2
  import { parseNumericRange } from "@temelj/iterator";
2
- import * as z$1 from "zod";
3
3
  //#region src/plugins/syntax-highlight/code-meta.ts
4
4
  function extractCodeMeta(node) {
5
5
  return parseMetaString(node?.data?.meta ?? node?.properties?.metastring ?? "");
6
6
  }
7
- const metaSchema = z$1.object({
8
- highlight: z$1.optional(z$1.string()),
9
- showLineNumbers: z$1.optional(z$1.union([z$1.boolean(), z$1.string()])),
10
- commandLine: z$1.optional(z$1.union([z$1.boolean(), z$1.string()])),
11
- fileName: z$1.optional(z$1.string())
12
- });
7
+ const stringOrBoolean = ss.union([ss.string("Expected string or boolean"), ss.boolean("Expected string or boolean")], "Expected string or boolean");
8
+ const metaSchema = ss.object({
9
+ highlight: ss.optional(ss.string("Expected string")),
10
+ showLineNumbers: ss.optional(stringOrBoolean),
11
+ commandLine: ss.optional(stringOrBoolean),
12
+ fileName: ss.optional(ss.string("Expected string"))
13
+ }, "Expected object");
13
14
  function parseMetaString(meta) {
14
15
  let raw;
15
16
  try {
@@ -17,7 +18,7 @@ function parseMetaString(meta) {
17
18
  } catch {
18
19
  return {};
19
20
  }
20
- const parsed = metaSchema.parse(raw);
21
+ const parsed = validateStandardSchemaSync(metaSchema, raw, "Code metadata validation failed");
21
22
  const options = {};
22
23
  if (parsed.highlight !== void 0) options.highlight = { lineRange: parseNumericRange(parsed.highlight) };
23
24
  if (parsed.showLineNumbers !== void 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"code-meta.mjs","names":["z"],"sources":["../../../src/plugins/syntax-highlight/code-meta.ts"],"sourcesContent":["import { type NumericRange, parseNumericRange } from \"@temelj/iterator\";\nimport * as z from \"zod\";\n\nimport type { HastElement } from \"../../types\";\n\nexport interface MdxCodeMeta {\n highlight?: MdxCodeMetaHighlightLine;\n showLineNumbers?: MdxCodeMetaLineNumbers;\n commandLine?: MdxCodeMetaCommandLine;\n fileName?: string;\n}\n\nexport type MdxCodeMetaHighlightLine = {\n lineRange: NumericRange;\n};\n\nexport type MdxCodeMetaLineNumbers = {\n lineRange: NumericRange;\n};\n\nexport type MdxCodeMetaCommandLine = {\n commandRange: NumericRange;\n};\n\nexport function extractCodeMeta(node: HastElement): MdxCodeMeta {\n const meta = node?.data?.meta ?? node?.properties?.metastring ?? \"\";\n return parseMetaString(meta as string);\n}\n\nconst metaSchema = z.object({\n highlight: z.optional(z.string()),\n showLineNumbers: z.optional(z.union([z.boolean(), z.string()])),\n commandLine: z.optional(z.union([z.boolean(), z.string()])),\n fileName: z.optional(z.string()),\n});\n\nfunction parseMetaString(meta: string): MdxCodeMeta {\n let raw: unknown;\n try {\n raw = JSON.parse(meta.trim());\n } catch {\n return {};\n }\n const parsed = metaSchema.parse(raw);\n const options: MdxCodeMeta = {};\n\n if (parsed.highlight !== undefined) {\n options.highlight = { lineRange: parseNumericRange(parsed.highlight) };\n }\n\n if (parsed.showLineNumbers !== undefined) {\n if (typeof parsed.showLineNumbers === \"string\") {\n options.showLineNumbers = {\n lineRange: parseNumericRange(parsed.showLineNumbers),\n };\n } else if (parsed.showLineNumbers) {\n options.showLineNumbers = { lineRange: [] };\n }\n }\n\n if (parsed.commandLine !== undefined) {\n if (typeof parsed.commandLine === \"string\") {\n options.commandLine = {\n commandRange: parseNumericRange(parsed.commandLine),\n };\n } else if (parsed.commandLine) {\n options.commandLine = { commandRange: [] };\n }\n }\n\n if (parsed.fileName !== undefined) {\n options.fileName = parsed.fileName.trim();\n }\n\n return options;\n}\n"],"mappings":";;;AAwBA,SAAgB,gBAAgB,MAAgC;AAE9D,QAAO,gBADM,MAAM,MAAM,QAAQ,MAAM,YAAY,cAAc,GAC3B;;AAGxC,MAAM,aAAaA,IAAE,OAAO;CAC1B,WAAWA,IAAE,SAASA,IAAE,QAAQ,CAAC;CACjC,iBAAiBA,IAAE,SAASA,IAAE,MAAM,CAACA,IAAE,SAAS,EAAEA,IAAE,QAAQ,CAAC,CAAC,CAAC;CAC/D,aAAaA,IAAE,SAASA,IAAE,MAAM,CAACA,IAAE,SAAS,EAAEA,IAAE,QAAQ,CAAC,CAAC,CAAC;CAC3D,UAAUA,IAAE,SAASA,IAAE,QAAQ,CAAC;CACjC,CAAC;AAEF,SAAS,gBAAgB,MAA2B;CAClD,IAAI;AACJ,KAAI;AACF,QAAM,KAAK,MAAM,KAAK,MAAM,CAAC;SACvB;AACN,SAAO,EAAE;;CAEX,MAAM,SAAS,WAAW,MAAM,IAAI;CACpC,MAAM,UAAuB,EAAE;AAE/B,KAAI,OAAO,cAAc,KAAA,EACvB,SAAQ,YAAY,EAAE,WAAW,kBAAkB,OAAO,UAAU,EAAE;AAGxE,KAAI,OAAO,oBAAoB,KAAA;MACzB,OAAO,OAAO,oBAAoB,SACpC,SAAQ,kBAAkB,EACxB,WAAW,kBAAkB,OAAO,gBAAgB,EACrD;WACQ,OAAO,gBAChB,SAAQ,kBAAkB,EAAE,WAAW,EAAE,EAAE;;AAI/C,KAAI,OAAO,gBAAgB,KAAA;MACrB,OAAO,OAAO,gBAAgB,SAChC,SAAQ,cAAc,EACpB,cAAc,kBAAkB,OAAO,YAAY,EACpD;WACQ,OAAO,YAChB,SAAQ,cAAc,EAAE,cAAc,EAAE,EAAE;;AAI9C,KAAI,OAAO,aAAa,KAAA,EACtB,SAAQ,WAAW,OAAO,SAAS,MAAM;AAG3C,QAAO"}
1
+ {"version":3,"file":"code-meta.mjs","names":[],"sources":["../../../src/plugins/syntax-highlight/code-meta.ts"],"sourcesContent":["import { type NumericRange, parseNumericRange } from \"@temelj/iterator\";\nimport { ss, validateStandardSchemaSync } from \"@temelj/standard-schema\";\n\nimport type { HastElement } from \"../../types\";\n\nexport interface MdxCodeMeta {\n highlight?: MdxCodeMetaHighlightLine;\n showLineNumbers?: MdxCodeMetaLineNumbers;\n commandLine?: MdxCodeMetaCommandLine;\n fileName?: string;\n}\n\nexport type MdxCodeMetaHighlightLine = {\n lineRange: NumericRange;\n};\n\nexport type MdxCodeMetaLineNumbers = {\n lineRange: NumericRange;\n};\n\nexport type MdxCodeMetaCommandLine = {\n commandRange: NumericRange;\n};\n\nexport function extractCodeMeta(node: HastElement): MdxCodeMeta {\n const meta = node?.data?.meta ?? node?.properties?.metastring ?? \"\";\n return parseMetaString(meta as string);\n}\n\nconst stringOrBoolean = ss.union(\n [ss.string(\"Expected string or boolean\"), ss.boolean(\"Expected string or boolean\")],\n \"Expected string or boolean\",\n);\n\nconst metaSchema = ss.object(\n {\n highlight: ss.optional(ss.string(\"Expected string\")),\n showLineNumbers: ss.optional(stringOrBoolean),\n commandLine: ss.optional(stringOrBoolean),\n fileName: ss.optional(ss.string(\"Expected string\")),\n },\n \"Expected object\",\n);\n\nfunction parseMetaString(meta: string): MdxCodeMeta {\n let raw: unknown;\n try {\n raw = JSON.parse(meta.trim());\n } catch {\n return {};\n }\n const parsed = validateStandardSchemaSync(metaSchema, raw, \"Code metadata validation failed\");\n const options: MdxCodeMeta = {};\n\n if (parsed.highlight !== undefined) {\n options.highlight = { lineRange: parseNumericRange(parsed.highlight) };\n }\n\n if (parsed.showLineNumbers !== undefined) {\n if (typeof parsed.showLineNumbers === \"string\") {\n options.showLineNumbers = {\n lineRange: parseNumericRange(parsed.showLineNumbers),\n };\n } else if (parsed.showLineNumbers) {\n options.showLineNumbers = { lineRange: [] };\n }\n }\n\n if (parsed.commandLine !== undefined) {\n if (typeof parsed.commandLine === \"string\") {\n options.commandLine = {\n commandRange: parseNumericRange(parsed.commandLine),\n };\n } else if (parsed.commandLine) {\n options.commandLine = { commandRange: [] };\n }\n }\n\n if (parsed.fileName !== undefined) {\n options.fileName = parsed.fileName.trim();\n }\n\n return options;\n}\n"],"mappings":";;;AAwBA,SAAgB,gBAAgB,MAAgC;CAE9D,OAAO,gBADM,MAAM,MAAM,QAAQ,MAAM,YAAY,cAAc,EAC5B;AACvC;AAEA,MAAM,kBAAkB,GAAG,MACzB,CAAC,GAAG,OAAO,4BAA4B,GAAG,GAAG,QAAQ,4BAA4B,CAAC,GAClF,4BACF;AAEA,MAAM,aAAa,GAAG,OACpB;CACE,WAAW,GAAG,SAAS,GAAG,OAAO,iBAAiB,CAAC;CACnD,iBAAiB,GAAG,SAAS,eAAe;CAC5C,aAAa,GAAG,SAAS,eAAe;CACxC,UAAU,GAAG,SAAS,GAAG,OAAO,iBAAiB,CAAC;AACpD,GACA,iBACF;AAEA,SAAS,gBAAgB,MAA2B;CAClD,IAAI;CACJ,IAAI;EACF,MAAM,KAAK,MAAM,KAAK,KAAK,CAAC;CAC9B,QAAQ;EACN,OAAO,CAAC;CACV;CACA,MAAM,SAAS,2BAA2B,YAAY,KAAK,iCAAiC;CAC5F,MAAM,UAAuB,CAAC;CAE9B,IAAI,OAAO,cAAc,KAAA,GACvB,QAAQ,YAAY,EAAE,WAAW,kBAAkB,OAAO,SAAS,EAAE;CAGvE,IAAI,OAAO,oBAAoB,KAAA;MACzB,OAAO,OAAO,oBAAoB,UACpC,QAAQ,kBAAkB,EACxB,WAAW,kBAAkB,OAAO,eAAe,EACrD;OACK,IAAI,OAAO,iBAChB,QAAQ,kBAAkB,EAAE,WAAW,CAAC,EAAE;CAAA;CAI9C,IAAI,OAAO,gBAAgB,KAAA;MACrB,OAAO,OAAO,gBAAgB,UAChC,QAAQ,cAAc,EACpB,cAAc,kBAAkB,OAAO,WAAW,EACpD;OACK,IAAI,OAAO,aAChB,QAAQ,cAAc,EAAE,cAAc,CAAC,EAAE;CAAA;CAI7C,IAAI,OAAO,aAAa,KAAA,GACtB,QAAQ,WAAW,OAAO,SAAS,KAAK;CAG1C,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.mts","names":[],"sources":["../../../src/plugins/syntax-highlight/plugin.ts"],"mappings":";;;;;;KAoBK,gBAAA,GAAmB,OAAA,CACtB,iBAAA,CAAkB,eAAA,EAAiB,YAAA;AAAA,KAGhC,mBAAA;AAPoD;;;AAAA,UAYxC,4BAAA;EARoB;;;;EAanC,qBAAA,GAAwB,mBAAA;EAdF;;;EAmBtB,SAAA;IACE,WAAA,GAAc,mCAAA;EAAA;EAhBb;;;EAsBH,WAAA;IAtBsB;AAKxB;;;IAsBI,iBAAA;IAXc;;;IAed,SAAA;EAAA;EArBsB;;;EA2BxB,WAAA;IAfA;;;IAmBE,SAAA;EAAA;EAMF;;;EAAA,gBAAA,GAAmB,gBAAA;AAAA;;;;cAMR,qBAAA,EAAuB,MAAA,EACjC,4BAAA,IACD,IAAA,EACA,IAAA"}
1
+ {"version":3,"file":"plugin.d.mts","names":[],"sources":["../../../src/plugins/syntax-highlight/plugin.ts"],"mappings":";;;;;;KAsBK,gBAAA,GAAmB,OAAA,CAAQ,iBAAA,CAAkB,eAAA,EAAiB,YAAA;AAAA,KAE9D,mBAAA;AANoD;;;AAAA,UAWxC,4BAAA;EAPkD;;;;EAYjE,qBAAA,GAAwB,mBAAA;EAZF;;;EAiBtB,SAAA;IACE,WAAA,GAAc,mCAAA;EAAA;EAhBb;;;EAsBH,WAAA;IAtBsB;AAKxB;;;IAsBI,iBAAA;IAXc;;;IAed,SAAA;EAAA;EArBsB;;;EA2BxB,WAAA;IAfA;;;IAmBE,SAAA;EAAA;EAMF;;;EAAA,gBAAA,GAAmB,gBAAA;AAAA;;;;cAMR,qBAAA,EAAuB,MAAA,EAAQ,4BAAA,IAAgC,IAAA,EAAU,IAAA"}
@@ -26,11 +26,11 @@ const syntaxHighlightPlugin = (options = {}) => {
26
26
  light: "github-light-default",
27
27
  dark: "github-dark-default"
28
28
  },
29
- ...options.shikiHastOptions ?? {}
29
+ ...options.shikiHastOptions
30
30
  };
31
31
  if (hastOptions.transformers === void 0) hastOptions.transformers = [];
32
32
  if (options.highlight) hastOptions.transformers.push(transformerNotationHighlight({
33
- ...options.highlight.transformer ?? {},
33
+ ...options.highlight.transformer,
34
34
  matchAlgorithm: "v3"
35
35
  }));
36
36
  let lineCount = 0;
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.mjs","names":["hastToString"],"sources":["../../../src/plugins/syntax-highlight/plugin.ts"],"sourcesContent":["import {\n type TransformerNotationHighlightOptions,\n transformerNotationHighlight,\n} from \"@shikijs/transformers\";\nimport { numericRangeContains } from \"@temelj/iterator\";\nimport type { Properties } from \"hast\";\nimport { toString as hastToString } from \"hast-util-to-string\";\nimport {\n type BundledLanguage,\n type BundledTheme,\n type CodeToHastOptions,\n codeToHast,\n ShikiError,\n} from \"shiki\";\nimport type { Plugin } from \"unified\";\nimport { visit } from \"unist-util-visit\";\n\nimport type { HastElement, HastNode } from \"../../types\";\nimport { extractCodeMeta } from \"./code-meta\";\n\ntype ShikiHastOptions = Partial<\n CodeToHastOptions<BundledLanguage, BundledTheme>\n>;\n\ntype SyntaxDataAttribute = \"source-code\" | \"language\" | \"line-count\";\n\n/**\n * Options for {@linkcode syntaxHighlightPlugin}.\n */\nexport interface SyntaxHighlightPluginOptions {\n /**\n * Include these data attributes on the `<pre>` element.\n * These might be useful to have when building custom code components.\n */\n includeDataAttributes?: SyntaxDataAttribute[];\n\n /**\n * Options for the highlight transformer.\n */\n highlight?: {\n transformer?: TransformerNotationHighlightOptions;\n };\n\n /**\n * Options for line numbers.\n */\n lineNumbers?: {\n /**\n * The name of the variable that contains the line number width.\n * This is used for padding the line numbers so that they align.\n */\n widthVariableName?: string;\n /**\n * A class name to be added to the line numbers.\n */\n className?: string;\n };\n\n /**\n * Options for command line.\n */\n commandLine?: {\n /**\n * A class name to be added to the prompt line.\n */\n className?: string;\n };\n\n /**\n * Extra options for the Shiki hast transformer.\n */\n shikiHastOptions?: ShikiHastOptions;\n}\n\n/**\n * A plugin that highlights code blocks using Shiki.\n */\nexport const syntaxHighlightPlugin: Plugin<\n [SyntaxHighlightPluginOptions?],\n HastNode,\n HastNode\n> = (options: SyntaxHighlightPluginOptions = {}) => {\n return async (tree) => {\n const promises: Promise<void>[] = [];\n\n async function visitor(\n node: HastElement,\n index: number,\n parent: HastElement,\n ): Promise<void> {\n if (!parent || index === null || node.tagName !== \"pre\") {\n return;\n }\n\n const codeNode = node.children[0];\n if (\n !codeNode ||\n codeNode.type !== \"element\" ||\n codeNode.tagName !== \"code\" ||\n !codeNode.properties\n ) {\n return;\n }\n\n const lang = getNodeLanguage(codeNode);\n if (typeof lang !== \"string\") {\n return;\n }\n\n const meta = extractCodeMeta(codeNode);\n\n const sourceCode = hastToString(codeNode).trim();\n\n const hastOptions: CodeToHastOptions<BundledLanguage, BundledTheme> = {\n tokenizeTimeLimit: 10_000,\n lang,\n themes: {\n light: \"github-light-default\",\n dark: \"github-dark-default\",\n },\n ...(options.shikiHastOptions ?? {}),\n };\n if (hastOptions.transformers === undefined) {\n hastOptions.transformers = [];\n }\n\n if (options.highlight) {\n hastOptions.transformers.push(\n transformerNotationHighlight({\n ...(options.highlight.transformer ?? {}),\n matchAlgorithm: \"v3\",\n }),\n );\n }\n\n let lineCount = 0;\n hastOptions.transformers.push({\n code(node): void {\n let lineIndex = 0;\n for (const line of node.children.filter(\n (node: HastNode) => node.type === \"element\",\n ) as HastElement[]) {\n line.properties[\"data-line\"] = lineIndex + 1;\n\n if (options.highlight) {\n if (\n meta.highlight &&\n numericRangeContains(meta.highlight.lineRange, lineIndex)\n ) {\n this.addClassToHast(\n line,\n options.highlight.transformer?.classActiveLine ??\n \"highlighted\",\n );\n }\n }\n\n if (\n options.lineNumbers &&\n meta.showLineNumbers &&\n (meta.showLineNumbers.lineRange.length === 0 ||\n numericRangeContains(meta.showLineNumbers.lineRange, lineIndex))\n ) {\n this.addClassToHast(\n line,\n options.lineNumbers.className ?? \"line-number\",\n );\n }\n\n if (\n options.commandLine &&\n meta.commandLine &&\n (meta.commandLine.commandRange.length === 0 ||\n numericRangeContains(meta.commandLine.commandRange, lineIndex))\n ) {\n this.addClassToHast(\n line,\n options.commandLine.className ?? \"line-command\",\n );\n }\n\n lineIndex++;\n }\n\n lineCount = lineIndex;\n\n if (meta.showLineNumbers) {\n if (typeof node.properties.style !== \"string\") {\n node.properties.style = \"\";\n }\n node.properties.style += `--${\n options.lineNumbers?.widthVariableName ?? \"line-number-width\"\n }:${1 + Math.floor(Math.log10(this.lines.length))}ch;`;\n }\n },\n });\n\n try {\n const hast = await codeToHast(sourceCode, hastOptions);\n\n // Insert highlighted code into parent\n const hastPre = hast.children[0] as HastElement;\n\n const properties: Properties = {\n ...hastPre.properties,\n dataFileName: meta.fileName,\n };\n if (options.includeDataAttributes) {\n if (options.includeDataAttributes.includes(\"language\")) {\n properties.dataLanguage = lang;\n }\n if (options.includeDataAttributes.includes(\"source-code\")) {\n properties.dataSourceCode = sourceCode;\n }\n if (options.includeDataAttributes.includes(\"line-count\")) {\n properties.dataLineCount = lineCount;\n }\n }\n\n parent.children.splice(index, 1, {\n ...hastPre,\n properties,\n });\n } catch (error) {\n if (error instanceof ShikiError) {\n if (\n error.message.includes(\n \"Language `math` is not included in this bundle\",\n )\n ) {\n // Skip if the block is math block.\n return;\n }\n }\n throw error;\n }\n }\n\n visit(\n tree,\n \"element\",\n (node: HastElement, index: number, parent: HastElement) => {\n promises.push(visitor(node, index, parent));\n },\n );\n\n await Promise.all(promises);\n\n return tree;\n };\n};\n\nfunction getNodeLanguage(node: HastElement): string | undefined {\n if (!Array.isArray(node.properties.className)) {\n return undefined;\n }\n const prefix = \"language-\";\n const language = node.properties.className.find(\n (name) => typeof name === \"string\" && name.startsWith(prefix),\n ) as string | undefined;\n if (typeof language === \"string\") {\n return language.slice(prefix.length);\n }\n return language;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"plugin.mjs","names":["hastToString"],"sources":["../../../src/plugins/syntax-highlight/plugin.ts"],"sourcesContent":["import type { Properties } from \"hast\";\nimport type { Plugin } from \"unified\";\n\nimport {\n type TransformerNotationHighlightOptions,\n transformerNotationHighlight,\n} from \"@shikijs/transformers\";\nimport { numericRangeContains } from \"@temelj/iterator\";\nimport { toString as hastToString } from \"hast-util-to-string\";\nimport {\n type BundledLanguage,\n type BundledTheme,\n type CodeToHastOptions,\n codeToHast,\n ShikiError,\n} from \"shiki\";\nimport { visit } from \"unist-util-visit\";\n\nimport type { HastElement, HastNode } from \"../../types\";\n\nimport { extractCodeMeta } from \"./code-meta\";\n\ntype ShikiHastOptions = Partial<CodeToHastOptions<BundledLanguage, BundledTheme>>;\n\ntype SyntaxDataAttribute = \"source-code\" | \"language\" | \"line-count\";\n\n/**\n * Options for {@linkcode syntaxHighlightPlugin}.\n */\nexport interface SyntaxHighlightPluginOptions {\n /**\n * Include these data attributes on the `<pre>` element.\n * These might be useful to have when building custom code components.\n */\n includeDataAttributes?: SyntaxDataAttribute[];\n\n /**\n * Options for the highlight transformer.\n */\n highlight?: {\n transformer?: TransformerNotationHighlightOptions;\n };\n\n /**\n * Options for line numbers.\n */\n lineNumbers?: {\n /**\n * The name of the variable that contains the line number width.\n * This is used for padding the line numbers so that they align.\n */\n widthVariableName?: string;\n /**\n * A class name to be added to the line numbers.\n */\n className?: string;\n };\n\n /**\n * Options for command line.\n */\n commandLine?: {\n /**\n * A class name to be added to the prompt line.\n */\n className?: string;\n };\n\n /**\n * Extra options for the Shiki hast transformer.\n */\n shikiHastOptions?: ShikiHastOptions;\n}\n\n/**\n * A plugin that highlights code blocks using Shiki.\n */\nexport const syntaxHighlightPlugin: Plugin<[SyntaxHighlightPluginOptions?], HastNode, HastNode> = (\n options: SyntaxHighlightPluginOptions = {},\n) => {\n return async (tree) => {\n const promises: Promise<void>[] = [];\n\n async function visitor(node: HastElement, index: number, parent: HastElement): Promise<void> {\n if (!parent || index === null || node.tagName !== \"pre\") {\n return;\n }\n\n const codeNode = node.children[0];\n if (\n !codeNode ||\n codeNode.type !== \"element\" ||\n codeNode.tagName !== \"code\" ||\n !codeNode.properties\n ) {\n return;\n }\n\n const lang = getNodeLanguage(codeNode);\n if (typeof lang !== \"string\") {\n return;\n }\n\n const meta = extractCodeMeta(codeNode);\n\n const sourceCode = hastToString(codeNode).trim();\n\n const hastOptions: CodeToHastOptions<BundledLanguage, BundledTheme> = {\n tokenizeTimeLimit: 10_000,\n lang,\n themes: {\n light: \"github-light-default\",\n dark: \"github-dark-default\",\n },\n ...options.shikiHastOptions,\n };\n if (hastOptions.transformers === undefined) {\n hastOptions.transformers = [];\n }\n\n if (options.highlight) {\n hastOptions.transformers.push(\n transformerNotationHighlight({\n ...options.highlight.transformer,\n matchAlgorithm: \"v3\",\n }),\n );\n }\n\n let lineCount = 0;\n hastOptions.transformers.push({\n code(node): void {\n let lineIndex = 0;\n for (const line of node.children.filter(\n (node: HastNode) => node.type === \"element\",\n ) as HastElement[]) {\n line.properties[\"data-line\"] = lineIndex + 1;\n\n if (options.highlight) {\n if (meta.highlight && numericRangeContains(meta.highlight.lineRange, lineIndex)) {\n this.addClassToHast(\n line,\n options.highlight.transformer?.classActiveLine ?? \"highlighted\",\n );\n }\n }\n\n if (\n options.lineNumbers &&\n meta.showLineNumbers &&\n (meta.showLineNumbers.lineRange.length === 0 ||\n numericRangeContains(meta.showLineNumbers.lineRange, lineIndex))\n ) {\n this.addClassToHast(line, options.lineNumbers.className ?? \"line-number\");\n }\n\n if (\n options.commandLine &&\n meta.commandLine &&\n (meta.commandLine.commandRange.length === 0 ||\n numericRangeContains(meta.commandLine.commandRange, lineIndex))\n ) {\n this.addClassToHast(line, options.commandLine.className ?? \"line-command\");\n }\n\n lineIndex++;\n }\n\n lineCount = lineIndex;\n\n if (meta.showLineNumbers) {\n if (typeof node.properties.style !== \"string\") {\n node.properties.style = \"\";\n }\n node.properties.style += `--${\n options.lineNumbers?.widthVariableName ?? \"line-number-width\"\n }:${1 + Math.floor(Math.log10(this.lines.length))}ch;`;\n }\n },\n });\n\n try {\n const hast = await codeToHast(sourceCode, hastOptions);\n\n // Insert highlighted code into parent\n const hastPre = hast.children[0] as HastElement;\n\n const properties: Properties = {\n ...hastPre.properties,\n dataFileName: meta.fileName,\n };\n if (options.includeDataAttributes) {\n if (options.includeDataAttributes.includes(\"language\")) {\n properties.dataLanguage = lang;\n }\n if (options.includeDataAttributes.includes(\"source-code\")) {\n properties.dataSourceCode = sourceCode;\n }\n if (options.includeDataAttributes.includes(\"line-count\")) {\n properties.dataLineCount = lineCount;\n }\n }\n\n parent.children.splice(index, 1, {\n ...hastPre,\n properties,\n });\n } catch (error) {\n if (error instanceof ShikiError) {\n if (error.message.includes(\"Language `math` is not included in this bundle\")) {\n // Skip if the block is math block.\n return;\n }\n }\n throw error;\n }\n }\n\n visit(tree, \"element\", (node: HastElement, index: number, parent: HastElement) => {\n promises.push(visitor(node, index, parent));\n });\n\n await Promise.all(promises);\n\n return tree;\n };\n};\n\nfunction getNodeLanguage(node: HastElement): string | undefined {\n if (!Array.isArray(node.properties.className)) {\n return undefined;\n }\n const prefix = \"language-\";\n const language = node.properties.className.find(\n (name) => typeof name === \"string\" && name.startsWith(prefix),\n ) as string | undefined;\n if (typeof language === \"string\") {\n return language.slice(prefix.length);\n }\n return language;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.mts","names":[],"sources":["../../../src/plugins/tree-processor/plugin.ts"],"mappings":";;;;KAKY,gBAAA,IACV,IAAA,EAAM,OAAA,EACN,KAAA,UACA,MAAA,EAAQ,OAAA,YACE,OAAA;;AAJZ;;KASK,0BAAA;EARG,qBAUN,OAAA,EAAS,gBAAA;AAAA;;;;;cAOE,mBAAA,EAAqB,MAAA,EAC/B,0BAAA,IACD,IAAA,EACA,IAAA"}
1
+ {"version":3,"file":"plugin.d.mts","names":[],"sources":["../../../src/plugins/tree-processor/plugin.ts"],"mappings":";;;;KAMY,gBAAA,IACV,IAAA,EAAM,OAAA,EACN,KAAA,UACA,MAAA,EAAQ,OAAA,YACE,OAAA;;AAJZ;;KASK,0BAAA;EARG,qBAUN,OAAA,EAAS,gBAAgB;AAAA;;;;;cAOd,mBAAA,EAAqB,MAAA,EAAQ,0BAAA,IAA8B,IAAA,EAAU,IAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.mjs","names":[],"sources":["../../../src/plugins/tree-processor/plugin.ts"],"sourcesContent":["import type { Plugin } from \"unified\";\nimport { visit } from \"unist-util-visit\";\n\nimport type { HastElement, HastNode } from \"../../types\";\n\nexport type MdxTreeProcessor = (\n node: HastElement,\n index: number,\n parent: HastElement,\n) => void | Promise<void>;\n\n/**\n * Options for {@linkcode treeProcessorPlugin}.\n */\ntype TreeProcessorPluginOptions = {\n /** The processor. */\n process: MdxTreeProcessor;\n};\n\n/**\n * A plugin that allows to process MDX tree nodes.\n * It can be async or sync.\n */\nexport const treeProcessorPlugin: Plugin<\n [TreeProcessorPluginOptions?],\n HastNode,\n HastNode\n> = ({ process } = { process: () => {} }) => {\n return async (tree) => {\n const promises: Promise<void>[] = [];\n\n visit(\n tree,\n \"element\",\n function visitor(\n node: HastElement,\n index: number,\n parent: HastElement,\n ): void {\n const p = process(node, index, parent);\n if (\n p instanceof Promise ||\n Object.prototype.toString.call(p) === \"[object Promise]\"\n ) {\n promises.push(p as Promise<void>);\n }\n },\n );\n\n await Promise.allSettled(promises);\n\n return tree;\n };\n};\n"],"mappings":";;;;;;AAuBA,MAAa,uBAIR,EAAE,YAAY,EAAE,eAAe,IAAI,KAAK;AAC3C,QAAO,OAAO,SAAS;EACrB,MAAM,WAA4B,EAAE;AAEpC,QACE,MACA,WACA,SAAS,QACP,MACA,OACA,QACM;GACN,MAAM,IAAI,QAAQ,MAAM,OAAO,OAAO;AACtC,OACE,aAAa,WACb,OAAO,UAAU,SAAS,KAAK,EAAE,KAAK,mBAEtC,UAAS,KAAK,EAAmB;IAGtC;AAED,QAAM,QAAQ,WAAW,SAAS;AAElC,SAAO"}
1
+ {"version":3,"file":"plugin.mjs","names":[],"sources":["../../../src/plugins/tree-processor/plugin.ts"],"sourcesContent":["import type { Plugin } from \"unified\";\n\nimport { visit } from \"unist-util-visit\";\n\nimport type { HastElement, HastNode } from \"../../types\";\n\nexport type MdxTreeProcessor = (\n node: HastElement,\n index: number,\n parent: HastElement,\n) => void | Promise<void>;\n\n/**\n * Options for {@linkcode treeProcessorPlugin}.\n */\ntype TreeProcessorPluginOptions = {\n /** The processor. */\n process: MdxTreeProcessor;\n};\n\n/**\n * A plugin that allows to process MDX tree nodes.\n * It can be async or sync.\n */\nexport const treeProcessorPlugin: Plugin<[TreeProcessorPluginOptions?], HastNode, HastNode> = (\n { process } = { process: () => {} },\n) => {\n return async (tree) => {\n const promises: Promise<void>[] = [];\n\n visit(\n tree,\n \"element\",\n function visitor(node: HastElement, index: number, parent: HastElement): void {\n const p = process(node, index, parent);\n if (p instanceof Promise || Object.prototype.toString.call(p) === \"[object Promise]\") {\n promises.push(p as Promise<void>);\n }\n },\n );\n\n await Promise.allSettled(promises);\n\n return tree;\n };\n};\n"],"mappings":";;;;;;AAwBA,MAAa,uBACX,EAAE,YAAY,EAAE,eAAe,CAAC,EAAE,MAC/B;CACH,OAAO,OAAO,SAAS;EACrB,MAAM,WAA4B,CAAC;EAEnC,MACE,MACA,WACA,SAAS,QAAQ,MAAmB,OAAe,QAA2B;GAC5E,MAAM,IAAI,QAAQ,MAAM,OAAO,MAAM;GACrC,IAAI,aAAa,WAAW,OAAO,UAAU,SAAS,KAAK,CAAC,MAAM,oBAChE,SAAS,KAAK,CAAkB;EAEpC,CACF;EAEA,MAAM,QAAQ,WAAW,QAAQ;EAEjC,OAAO;CACT;AACF"}
package/dist/types.d.mts CHANGED
@@ -1,2 +1,2 @@
1
1
  import { Element, Node } from "hast";
2
- export { type Element as HastElement, type Node as HastNode };
2
+ export type { Element as HastElement, Node as HastNode };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@temelj/mdx",
3
- "version": "0.11.1",
3
+ "version": "0.12.0",
4
4
  "description": "MDX processing utilities.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -20,32 +20,34 @@
20
20
  },
21
21
  "dependencies": {
22
22
  "@mdx-js/mdx": "^3.1.1",
23
- "@shikijs/transformers": "^4.0.2",
23
+ "@shikijs/transformers": "^4.2.0",
24
+ "@standard-schema/spec": "^1.1.0",
24
25
  "@types/hast": "^3.0.4",
25
26
  "github-slugger": "^2.0.0",
26
27
  "hast": "^1.0.0",
27
28
  "hast-util-to-string": "^3.0.1",
29
+ "rehype-katex": "^7.0.1",
28
30
  "remark-frontmatter": "^5.0.0",
29
31
  "remark-gfm": "^4.0.1",
30
32
  "remark-math": "^6.0.0",
31
33
  "remark-parse": "^11.0.0",
32
- "rehype-katex": "^7.0.1",
33
- "shiki": "^4.0.2",
34
+ "shiki": "^4.2.0",
34
35
  "unified": "^11.0.5",
35
36
  "unist-util-remove": "^4.0.0",
36
37
  "unist-util-visit": "^5.1.0",
37
38
  "vfile": "^6.0.3",
38
39
  "vfile-matter": "^5.0.1",
39
- "zod": "^4.3.6",
40
- "@temelj/iterator": "0.11.0"
40
+ "@temelj/iterator": "0.12.0",
41
+ "@temelj/standard-schema": "0.1.0"
41
42
  },
42
43
  "devDependencies": {
43
44
  "@types/mdast": "^4.0.4",
44
- "@types/node": "25.5.0",
45
+ "@types/node": "25.9.2",
45
46
  "shx": "^0.4.0",
46
- "tsdown": "^0.21.7",
47
- "typescript": "^6.0.2",
48
- "vitest": "^4.1.2"
47
+ "tsdown": "^0.22.2",
48
+ "typescript": "^6.0.3",
49
+ "vitest": "^4.1.8",
50
+ "zod": "^4.4.3"
49
51
  },
50
52
  "scripts": {
51
53
  "build": "tsdown",