remark-mdat 2.0.0-preview.1 → 2.0.0-preview.2

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
@@ -7,6 +7,14 @@ import { Plugin } from "unified";
7
7
 
8
8
  //#region src/lib/mdat/rules.d.ts
9
9
  type SimplifyDeep<T> = Simplify<MergeDeep<T, T>>;
10
+ /**
11
+ * Context passed to rule content functions during expansion.
12
+ */
13
+ type RuleContext = {
14
+ /** File path of the source document, if known. */filePath: string | undefined; /** Parsed YAML frontmatter from the document, if present. */
15
+ frontmatter: Record<string, unknown> | undefined; /** The full mdast AST of the document. Do not mutate. */
16
+ tree: Root;
17
+ };
10
18
  /**
11
19
  * Strict normalized rules used internally.
12
20
  * Rules normalized to a form with async content functions and other default metadata
@@ -17,7 +25,7 @@ type NormalizedRule = {
17
25
  * The function that generates the expanded Markdown string.
18
26
  * For 'compound' rules, this can be an array of rules (without keywords).
19
27
  */
20
- content: ((options: JsonValue, tree: Root) => Promise<string>) | NormalizedRule[];
28
+ content: ((options: JsonValue, context: RuleContext) => Promise<string>) | NormalizedRule[];
21
29
  /**
22
30
  * The order in which the rule should be applied during processing
23
31
  * Helpful if a rule depends on the presence of content generated by another rule
@@ -29,7 +37,7 @@ type Rule =
29
37
  /**
30
38
  * Function that returns the Markdown string to expand at the comment site.
31
39
  */
32
- ((options: JsonValue, tree: Root) => Promise<string> | string)
40
+ ((options: JsonValue, context: RuleContext) => Promise<string> | string)
33
41
  /**
34
42
  * Compound rules may be defined an array of rules, without keywords.
35
43
  * Can be defined at the top level, if no validation metadata is required, or as the 'content' value
@@ -54,11 +62,11 @@ type Rule =
54
62
  * `<!-- keyword({something: true}) -->` or
55
63
  * `<!-- keyword {something: true}-->`
56
64
  * Sets options to {something: true}
57
- * @param tree
58
- * Markdown (mdast) abstract syntax tree containing the entire parsed document. Useful for expanders that need the entire document context, such as when generating a table of contents. Do not mutate the AST, instead return a new string.
65
+ * @param context
66
+ * Rule context containing the mdast AST, parsed frontmatter, and file path.
59
67
  * @returns A string with the generated content. The string will be parsed as Markdown and inserted into the document at the comment's location.
60
68
  */
61
- content: ((options: JsonValue, tree: Root) => Promise<string> | string) | Rule[] | string;
69
+ content: ((options: JsonValue, context: RuleContext) => Promise<string> | string) | Rule[] | string;
62
70
  /**
63
71
  * The order in which the rule should be applied during processing.
64
72
  * Defaults to 0.
@@ -165,4 +173,4 @@ type Options = Rules;
165
173
  */
166
174
  declare const remarkMdat: Plugin<[Options], Root>;
167
175
  //#endregion
168
- export { type MdatFileReport, type MdatMessage, type NormalizedRule, type NormalizedRules, type Options, type Rule, type Rules, type SimplifyDeep, remarkMdat as default, getMdatReports, getSoleRule, getSoleRuleKey, mdat, mdatClean, mdatExpand, mdatSplit, rulesSchema as optionsSchema, rulesSchema, reporterMdat, setLogger };
176
+ export { type MdatFileReport, type MdatMessage, type NormalizedRule, type NormalizedRules, type Options, type Rule, type RuleContext, type Rules, type SimplifyDeep, remarkMdat as default, getMdatReports, getSoleRule, getSoleRuleKey, mdat, mdatClean, mdatExpand, mdatSplit, rulesSchema as optionsSchema, rulesSchema, reporterMdat, setLogger };
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ import picocolors from "picocolors";
4
4
  import { createLogger, injectionHelper } from "lognow";
5
5
  import json5 from "json5";
6
6
  import { VFileMessage } from "vfile-message";
7
+ import { matter } from "gray-matter-es";
7
8
  import { remark } from "remark";
8
9
  import remarkGfm from "remark-gfm";
9
10
  import { z } from "zod";
@@ -212,7 +213,7 @@ function normalizeRules(rules) {
212
213
  order: 0
213
214
  };
214
215
  else if (typeof rule === "function") normalizedRules[keyword] = {
215
- content: async (options, tree) => rule(options, tree),
216
+ content: async (options, context) => rule(options, context),
216
217
  order: 0
217
218
  };
218
219
  else if (Array.isArray(rule)) normalizedRules[keyword] = {
@@ -232,7 +233,7 @@ function normalizeRules(rules) {
232
233
  else {
233
234
  const ruleContent = rule.content;
234
235
  normalizedRules[keyword] = {
235
- content: async (options, tree) => ruleContent(options, tree),
236
+ content: async (options, context) => ruleContent(options, context),
236
237
  order: rule.order ?? 0
237
238
  };
238
239
  }
@@ -275,25 +276,20 @@ const keywordSchema = z.string().check(z.refine((key) => !/^[/*#]/.test(key), {
275
276
  const rulesSchema = z.record(keywordSchema, ruleSchema).describe("MDAT Rules");
276
277
  const normalizedRulesSchema = z.record(keywordSchema, normalizedRuleSchema).describe("MDAT Rules");
277
278
  /**
278
- * Compound rule helpers, used in both "expand" and "check" utilities
279
+ * Compound rule helpers for expanding rule content.
279
280
  */
280
- async function getRuleContent(rule, options, tree, check = false) {
281
+ async function getRuleContent(rule, options, context) {
281
282
  if (Array.isArray(rule.content)) {
282
283
  const subruleContent = [];
283
284
  for (const [index, subrule] of rule.content.entries()) {
284
285
  const subruleOptions = Array.isArray(options) ? options.at(index) : void 0;
285
- try {
286
- subruleContent.push(await getRuleContent(subrule, subruleOptions ?? {}, tree));
287
- } catch (error) {
288
- if (check) throw error;
289
- }
286
+ subruleContent.push(await getRuleContent(subrule, subruleOptions ?? {}, context));
290
287
  }
291
288
  return subruleContent.join("\n\n");
292
289
  }
293
290
  try {
294
- return await rule.content(options, tree);
291
+ return await rule.content(options, context);
295
292
  } catch (error) {
296
- if (check) throw error;
297
293
  throw new Error("Failed to expand content", { cause: error });
298
294
  }
299
295
  }
@@ -340,6 +336,16 @@ function getSoleRecord(record) {
340
336
  async function mdatExpand(tree, file, rules) {
341
337
  validateRules(rules);
342
338
  const normalizedRules = normalizeRules(rules);
339
+ const frontmatter = (() => {
340
+ if (typeof file.value !== "string") return;
341
+ const { data } = matter(file.value);
342
+ return Object.keys(data).length > 0 ? data : void 0;
343
+ })();
344
+ const context = {
345
+ filePath: file.history.length > 0 ? file.path : void 0,
346
+ frontmatter,
347
+ tree
348
+ };
343
349
  const commentMarkers = [];
344
350
  visit(tree, "html", (node, index, parent) => {
345
351
  if (parent === void 0 || index === void 0) return CONTINUE;
@@ -357,7 +363,7 @@ async function mdatExpand(tree, file, rules) {
357
363
  const rule = normalizedRules[keyword];
358
364
  let newMarkdownString = "";
359
365
  try {
360
- newMarkdownString = await getRuleContent(rule, options, tree);
366
+ newMarkdownString = await getRuleContent(rule, options, context);
361
367
  if (newMarkdownString.trim() === "") saveLog(file, "error", "expand", `Got empty content when expanding ${html}`, node);
362
368
  } catch (error) {
363
369
  if (error instanceof Error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remark-mdat",
3
- "version": "2.0.0-preview.1",
3
+ "version": "2.0.0-preview.2",
4
4
  "description": "A remark plugin implementing the Markdown Autophagic Template (MDAT) system.",
5
5
  "keywords": [
6
6
  "mdat",
@@ -43,6 +43,7 @@
43
43
  "dependencies": {
44
44
  "@types/mdast": "^4.0.4",
45
45
  "@types/unist": "^3.0.3",
46
+ "gray-matter-es": "^0.2.1",
46
47
  "hast-util-from-html": "^2.0.3",
47
48
  "json5": "^2.2.3",
48
49
  "lognow": "^0.5.2",
@@ -67,7 +68,7 @@
67
68
  "vitest": "^4.1.1"
68
69
  },
69
70
  "engines": {
70
- "node": ">=20.19.0"
71
+ "node": ">=20.19.6"
71
72
  },
72
73
  "devEngines": {
73
74
  "runtime": {
@@ -82,7 +83,7 @@
82
83
  "fix": "ksc fix",
83
84
  "lint": "ksc lint",
84
85
  "release": "bumpp --commit 'Release: %s' && pnpm run build && NPM_AUTH_TOKEN=$(op read 'op://Personal/npm/token') && pnpm publish",
85
- "release-preview": "bumpp --preid preview --commit 'Release: %s' && NPM_AUTH_TOKEN=$(op read 'op://Personal/npm/token') && pnpm publish --tag preview",
86
+ "release-preview": "bumpp --preid preview --commit 'Release: %s' && pnpm run build && NPM_AUTH_TOKEN=$(op read 'op://Personal/npm/token') && pnpm publish --tag preview",
86
87
  "test": "vitest run"
87
88
  }
88
89
  }
package/readme.md CHANGED
@@ -128,8 +128,38 @@ const rules: Rules = {
128
128
  // Array: compound rule combining multiple sub-rules
129
129
  header: ['# My Project', () => getDescription()],
130
130
 
131
- // Function with document access: receives the full mdast tree
132
- toc: (_options, tree) => generateTocFromTree(tree),
131
+ // Function with context: access the document tree, frontmatter, and file path
132
+ toc: (_options, context) => generateTocFromTree(context.tree),
133
+ }
134
+ ```
135
+
136
+ #### Rule context
137
+
138
+ Rule content functions receive a `RuleContext` object as their second argument, providing access to the document being processed:
139
+
140
+ ```ts
141
+ type RuleContext = {
142
+ /** File path of the source document, if known. */
143
+ filePath: string | undefined
144
+ /** Parsed YAML frontmatter from the document, if present. */
145
+ frontmatter: Record<string, unknown> | undefined
146
+ /** The full mdast AST of the document. Do not mutate. */
147
+ tree: Root
148
+ }
149
+ ```
150
+
151
+ Frontmatter is automatically extracted from the raw Markdown source using [gray-matter-es](https://github.com/ryoppippi/gray-matter-es), so it works regardless of whether `remark-frontmatter` is in your pipeline. If the document has no frontmatter block, `context.frontmatter` is `undefined`.
152
+
153
+ ```ts
154
+ const rules: Rules = {
155
+ // Access frontmatter values
156
+ title: (_options, context) => `# ${context.frontmatter?.title ?? 'Untitled'}`,
157
+
158
+ // Use the file path
159
+ source: (_options, context) => `Source: ${context.filePath ?? 'unknown'}`,
160
+
161
+ // Traverse the AST
162
+ toc: (_options, context) => generateTocFromTree(context.tree),
133
163
  }
134
164
  ```
135
165