remark-mdat 2.2.4 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -3,8 +3,8 @@ import { z } from "zod";
3
3
  import { Root } from "mdast";
4
4
  import { VFile } from "vfile";
5
5
  import { JsonValue, MergeDeep, Simplify } from "type-fest";
6
+ import "unist";
6
7
  import { Plugin } from "unified";
7
-
8
8
  //#region src/lib/mdat/rules.d.ts
9
9
  /**
10
10
  * Recursively simplifies a type for cleaner IDE hover display. Approximation of
@@ -15,8 +15,11 @@ type SimplifyDeep<T> = Simplify<MergeDeep<T, T>>;
15
15
  * Context passed to rule content functions during expansion.
16
16
  */
17
17
  type RuleContext = {
18
- /** File path of the source document, if known. */filePath: string | undefined; /** Parsed YAML frontmatter from the document, if present. */
19
- frontmatter: Record<string, unknown> | undefined; /** The full mdast AST of the document. Do not mutate. */
18
+ /** File path of the source document, if known. */
19
+ filePath: string | undefined;
20
+ /** Parsed YAML frontmatter from the document, if present. */
21
+ frontmatter: Record<string, unknown> | undefined;
22
+ /** The full mdast AST of the document. Do not mutate. */
20
23
  tree: Root;
21
24
  };
22
25
  /**
@@ -41,21 +44,21 @@ type Rule =
41
44
  /**
42
45
  * Function that returns the Markdown string to expand at the comment site.
43
46
  */
44
- ((options: JsonValue, context: RuleContext) => Promise<string> | string)
47
+ ((options: JsonValue, context: RuleContext) => Promise<string> | string) |
45
48
  /**
46
49
  * Compound rules may be defined an array of rules, without keywords. Can be
47
50
  * defined at the top level, if no validation metadata is required, or as the
48
51
  * 'content' value of a rule object with validation metadata.
49
52
  */
50
- | Rule[]
53
+ Rule[] |
51
54
  /**
52
55
  * The Markdown string to expand at the comment site.
53
56
  */
54
- | string
57
+ string |
55
58
  /**
56
59
  * Rule object with optional metadata.
57
60
  */
58
- | {
61
+ {
59
62
  /**
60
63
  * Gets content to expand into the comment. Can be a simple string for
61
64
  * direct replacement, a function that returns a string, or an async
@@ -152,8 +155,11 @@ declare const mdatClean: typeof mdatCollapse;
152
155
  //#region src/lib/mdast-utils/mdast-util-mdat-diff.d.ts
153
156
  /** Per-tag comparison result from {@link mdatDiff}. */
154
157
  type MdatDiffResult = {
155
- /** The keyword for this tag. */keyword: string; /** 1-based line number of the opening comment in the expanded document. */
156
- line: number; /** Comparison status. */
158
+ /** The keyword for this tag. */
159
+ keyword: string;
160
+ /** 1-based line number of the opening comment in the expanded document. */
161
+ line: number;
162
+ /** Comparison status. */
157
163
  status: 'added' | 'missing' | 'ok' | 'stale' | 'unexpanded';
158
164
  };
159
165
  /**
@@ -204,18 +210,28 @@ declare function setLogger(logger?: ILogBasic | ILogLayer<unknown>): void;
204
210
  //#region src/lib/mdat/mdat-log.d.ts
205
211
  /** A simplified representation of a {@link VFileMessage}. */
206
212
  type MdatMessage = {
207
- /** Starting column of the message origin. */column?: number; /** Severity level. */
208
- level: 'error' | 'info' | 'warn'; /** Starting line of the message origin. */
209
- line?: number; /** Human-readable description of the issue. */
210
- message: string; /** Namespace that produced the message (e.g. the rule name). */
213
+ /** Starting column of the message origin. */
214
+ column?: number;
215
+ /** Severity level. */
216
+ level: 'error' | 'info' | 'warn';
217
+ /** Starting line of the message origin. */
218
+ line?: number;
219
+ /** Human-readable description of the issue. */
220
+ message: string;
221
+ /** Namespace that produced the message (e.g. the rule name). */
211
222
  source?: string;
212
223
  };
213
224
  /** Aggregated processing report for a single file. */
214
225
  type MdatFileReport = {
215
- /** Output path if the file was written to a different location. */destinationPath?: string; /** Fatal errors that prevented successful processing. */
216
- errors: MdatMessage[]; /** Informational messages. */
217
- infos: MdatMessage[]; /** Original input file path. */
218
- sourcePath: string; /** Non-fatal warnings encountered during processing. */
226
+ /** Output path if the file was written to a different location. */
227
+ destinationPath?: string;
228
+ /** Fatal errors that prevented successful processing. */
229
+ errors: MdatMessage[];
230
+ /** Informational messages. */
231
+ infos: MdatMessage[];
232
+ /** Original input file path. */
233
+ sourcePath: string;
234
+ /** Non-fatal warnings encountered during processing. */
219
235
  warnings: MdatMessage[];
220
236
  };
221
237
  /** Converts an array of processed VFiles into {@link MdatFileReport} objects. */
package/dist/index.js CHANGED
@@ -102,12 +102,12 @@ function reporterMdat(files) {
102
102
  }
103
103
  function mdatMessageToLogString(sourcePath, mdatMessage) {
104
104
  const { column, level, line, message, source } = mdatMessage;
105
- const resolvedSource = source ? picocolors.gray(`[${source}] `) : "";
106
- const lineColumn = line && column ? `:${line}:${column}` : "";
105
+ const resolvedSource = source !== void 0 && source !== "" ? picocolors.gray(`[${source}] `) : "";
106
+ const lineColumn = line !== void 0 && line !== 0 && column !== void 0 && column !== 0 ? `:${line}:${column}` : "";
107
107
  return `${resolvedSource}${highlightComments(message, level)} ${picocolors.whiteBright(sourcePath + lineColumn)}`;
108
108
  }
109
109
  function highlightComments(text, level) {
110
- return text.replaceAll(/<!--.+-->/g, (match) => level === "info" ? picocolors.green(match) : level === "warn" ? picocolors.yellow(match) : picocolors.red(match));
110
+ return text.replaceAll(/<!--.+-->/gv, (match) => level === "info" ? picocolors.green(match) : level === "warn" ? picocolors.yellow(match) : picocolors.red(match));
111
111
  }
112
112
  //#endregion
113
113
  //#region src/lib/mdat/parse.ts
@@ -135,9 +135,9 @@ function parseCommentNode(node, parent) {
135
135
  throw new VFileMessage("Unknown error", node);
136
136
  }
137
137
  }
138
- const HTML_COMMENT_OPEN_REGEX = /^\s*<!-{2,}\s*/;
139
- const HTML_COMMENT_CLOSE_REGEX = /\s*-{2,}>\s*$/;
140
- const WHITESPACE_REGEX = /\s/;
138
+ const HTML_COMMENT_OPEN_REGEX = /^\s*<!-{2,}\s*/v;
139
+ const HTML_COMMENT_CLOSE_REGEX = /\s*-{2,}>\s*$/v;
140
+ const WHITESPACE_REGEX = /\s/v;
141
141
  /**
142
142
  * Parse any comment string into structured data. Comments using code-style
143
143
  * notation (`//`, `#`, `/*`) are ignored and return `undefined`.
@@ -147,13 +147,12 @@ const WHITESPACE_REGEX = /\s/;
147
147
  */
148
148
  function parseComment(text) {
149
149
  if (!isComment(text)) return;
150
- const closingPrefix = "/";
151
150
  const commentHtml = text.trim();
152
151
  const commentBody = commentHtml.replace(HTML_COMMENT_OPEN_REGEX, "").replace(HTML_COMMENT_CLOSE_REGEX, "");
153
152
  const parenIndex = commentBody.indexOf("(");
154
- const rawKeyword = parenIndex === -1 ? commentBody.split(WHITESPACE_REGEX)[0] : commentBody.slice(0, parenIndex).trim();
153
+ const rawKeyword = parenIndex === -1 ? commentBody.split(WHITESPACE_REGEX)[0] ?? "" : commentBody.slice(0, parenIndex).trim();
155
154
  if (rawKeyword.startsWith("//") || rawKeyword.startsWith("#") || rawKeyword.startsWith("/*")) return;
156
- const type = rawKeyword.startsWith(closingPrefix) ? "close" : "open";
155
+ const type = rawKeyword.startsWith("/") ? "close" : "open";
157
156
  let keyword = rawKeyword;
158
157
  if (type === "close") keyword = keyword.slice(1);
159
158
  let options = {};
@@ -221,7 +220,7 @@ function mdatCollapse(tree, file) {
221
220
  const NORMALIZED = Symbol("normalized");
222
221
  /** Check whether rules have already been normalized. */
223
222
  function isNormalized(rules) {
224
- return NORMALIZED in rules;
223
+ return Object.hasOwn(rules, NORMALIZED);
225
224
  }
226
225
  /**
227
226
  * Converts flexible {@link Rules} into strict {@link NormalizedRules} for
@@ -267,7 +266,7 @@ function validateRules(rules) {
267
266
  try {
268
267
  rulesSchema.parse(rules);
269
268
  } catch (error) {
270
- if (error instanceof Error) throw new TypeError(`Error validating rules: ${error.message}`);
269
+ if (error instanceof Error) throw new TypeError(`Error validating rules: ${error.message}`, { cause: error });
271
270
  }
272
271
  }
273
272
  const functionSchema = z.custom((value) => typeof value === "function");
@@ -284,7 +283,7 @@ const ruleSchema = z.lazy(() => z.union([
284
283
  order: z.number().optional()
285
284
  })
286
285
  ]));
287
- const COMMENT_PREFIX_REGEX = /^[/*#]/;
286
+ const COMMENT_PREFIX_REGEX = /^[\/*#]/v;
288
287
  const keywordSchema = z.string().check(z.refine((key) => !COMMENT_PREFIX_REGEX.test(key), { message: "Rule keywords must not start with \"/\", \"*\", or \"#\" — these prefixes are reserved for comment syntax" }));
289
288
  /** Zod schema for validating {@link Rules} records. */
290
289
  const rulesSchema = z.record(keywordSchema, ruleSchema).describe("MDAT Rules");
@@ -333,8 +332,9 @@ function getSoleRule(rules) {
333
332
  */
334
333
  function getSoleRuleKey(rules) {
335
334
  const keys = Object.keys(rules);
336
- if (keys.length !== 1) throw new Error(`Expected exactly one rule, found ${keys.length}`);
337
- return keys[0];
335
+ const [soleKey] = keys;
336
+ if (soleKey === void 0 || keys.length > 1) throw new Error(`Expected exactly one rule, found ${keys.length}`);
337
+ return soleKey;
338
338
  }
339
339
  /**
340
340
  * Get the sole entry in a record.
@@ -375,18 +375,22 @@ async function mdatExpand(tree, file, rules) {
375
375
  if (parent === void 0 || index === void 0) return CONTINUE;
376
376
  const commentMarker = parseCommentNode(node, parent);
377
377
  if (commentMarker?.type !== "open") return CONTINUE;
378
- if (normalizedRules[commentMarker.keyword] === void 0) {
378
+ const rule = normalizedRules[commentMarker.keyword];
379
+ if (rule === void 0) {
379
380
  saveLog(file, "warn", "expand", `Missing rule for: ${commentMarker.html}`, node);
380
381
  return CONTINUE;
381
382
  }
382
- commentMarkers.push(commentMarker);
383
+ commentMarkers.push({
384
+ marker: commentMarker,
385
+ rule
386
+ });
387
+ return CONTINUE;
383
388
  });
384
- commentMarkers.sort((a, b) => normalizedRules[a.keyword].order - normalizedRules[b.keyword].order);
389
+ commentMarkers.sort((a, b) => a.rule.order - b.rule.order);
385
390
  const parser = remark().use(remarkGfm);
386
- for (const comment of commentMarkers) {
387
- const { html, keyword, node, options, parent } = comment;
388
- const rule = normalizedRules[keyword];
389
- let newMarkdownString = "";
391
+ for (const { marker, rule } of commentMarkers) {
392
+ const { html, keyword, node, options, parent } = marker;
393
+ let newMarkdownString;
390
394
  try {
391
395
  newMarkdownString = await getRuleContent(rule, options, context, (warning) => {
392
396
  saveLog(file, "warn", "expand", `${html}: ${warning}`, node);
@@ -424,11 +428,12 @@ function mdatSplit(tree, file) {
424
428
  const htmlNodes = splitHtmlIntoMdastNodes(node);
425
429
  if (htmlNodes.length > 1) {
426
430
  saveLog(file, "warn", "split", "Multiple comments in a single HTML node.", node);
427
- parent.children.splice(index, 1, {
431
+ parent.children[index] = {
428
432
  children: htmlNodes,
429
433
  type: "paragraph"
430
- });
434
+ };
431
435
  }
436
+ return CONTINUE;
432
437
  });
433
438
  }
434
439
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remark-mdat",
3
- "version": "2.2.4",
3
+ "version": "3.0.1",
4
4
  "description": "A remark plugin implementing the Markdown Autophagic Template (MDAT) system.",
5
5
  "keywords": [
6
6
  "mdat",
@@ -46,11 +46,11 @@
46
46
  "gray-matter-es": "^0.2.1",
47
47
  "hast-util-from-html": "^2.0.3",
48
48
  "json5": "^2.2.3",
49
- "lognow": "^0.6.2",
49
+ "lognow": "^0.8.0",
50
50
  "picocolors": "^1.1.1",
51
51
  "remark": "^15.0.1",
52
52
  "remark-gfm": "^4.0.1",
53
- "type-fest": "^5.7.0",
53
+ "type-fest": "^5.8.0",
54
54
  "unified": "^11.0.5",
55
55
  "unist-util-visit": "^5.1.0",
56
56
  "vfile": "^6.0.3",
@@ -58,25 +58,19 @@
58
58
  "zod": "^4.4.3"
59
59
  },
60
60
  "devDependencies": {
61
- "@arethetypeswrong/core": "^0.18.3",
62
- "@kitschpatrol/shared-config": "^7.6.3",
63
- "@types/node": "~20.19.41",
64
- "@vitest/coverage-v8": "^4.1.8",
65
- "bumpp": "^11.1.0",
66
- "publint": "^0.3.21",
61
+ "@arethetypeswrong/core": "^0.18.5",
62
+ "@kitschpatrol/shared-config": "^8.6.0",
63
+ "@types/node": "~24.13.3",
64
+ "@vitest/coverage-v8": "^4.1.10",
65
+ "bumpp": "^12.1.1",
66
+ "publint": "^0.3.22",
67
67
  "shx": "^0.4.0",
68
- "tsdown": "^0.22.2",
69
- "typescript": "~5.9.3",
70
- "vitest": "^4.1.8"
68
+ "tsdown": "^0.22.14",
69
+ "typescript": "~6.0.3",
70
+ "vitest": "^4.1.10"
71
71
  },
72
72
  "engines": {
73
- "node": ">=20.19.6"
74
- },
75
- "devEngines": {
76
- "runtime": {
77
- "name": "node",
78
- "version": ">=22.22.2"
79
- }
73
+ "node": ">=24.16.0"
80
74
  },
81
75
  "scripts": {
82
76
  "bench": "vitest bench --run --no-file-parallelism --compare test/benchmarks/baseline.json",
package/readme.md CHANGED
@@ -6,8 +6,8 @@
6
6
 
7
7
  <!-- badges -->
8
8
 
9
- [![NPM Package remark-mdat](https://img.shields.io/npm/v/remark-mdat.svg)](https://npmjs.com/package/remark-mdat)
10
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/license/mit/)
9
+ [![NPM Package remark-mdat](https://img.shields.io/npm/v/remark-mdat.svg)](https://www.npmjs.com/package/remark-mdat)
10
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/license/mit)
11
11
  [![CI](https://github.com/kitschpatrol/remark-mdat/actions/workflows/ci.yml/badge.svg)](https://github.com/kitschpatrol/remark-mdat/actions/workflows/ci.yml)
12
12
 
13
13
  <!-- /badges -->
@@ -81,7 +81,7 @@ This plugin powers the higher-level [`mdat` package](https://github.com/kitschpa
81
81
 
82
82
  ### Dependencies
83
83
 
84
- This library is ESM only and requires Node 20.19.6+. It's designed to work with Remark 15. `remark-mdat` is implemented in TypeScript and bundles a complete set of type definitions.
84
+ This library is ESM only and requires Node 24.16.0+. It's designed to work with Remark 15. `remark-mdat` is implemented in TypeScript and bundles a complete set of type definitions.
85
85
 
86
86
  ### Installation
87
87