remark-mdat 2.0.0-preview.1 → 2.0.0-preview.3
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 +14 -6
- package/dist/index.js +27 -9
- package/package.json +4 -3
- package/readme.md +32 -2
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,
|
|
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,
|
|
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
|
|
58
|
-
*
|
|
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,
|
|
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,
|
|
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,
|
|
236
|
+
content: async (options, context) => ruleContent(options, context),
|
|
236
237
|
order: rule.order ?? 0
|
|
237
238
|
};
|
|
238
239
|
}
|
|
@@ -275,25 +276,30 @@ 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
|
-
*
|
|
279
|
+
* Expand rule content. For compound rules (content arrays), individual
|
|
280
|
+
* sub-rule failures are reported via `onWarning` and skipped. The entire
|
|
281
|
+
* expansion only fails if every sub-rule fails.
|
|
279
282
|
*/
|
|
280
|
-
async function getRuleContent(rule, options,
|
|
283
|
+
async function getRuleContent(rule, options, context, onWarning) {
|
|
281
284
|
if (Array.isArray(rule.content)) {
|
|
282
285
|
const subruleContent = [];
|
|
286
|
+
const errors = [];
|
|
283
287
|
for (const [index, subrule] of rule.content.entries()) {
|
|
284
288
|
const subruleOptions = Array.isArray(options) ? options.at(index) : void 0;
|
|
285
289
|
try {
|
|
286
|
-
subruleContent.push(await getRuleContent(subrule, subruleOptions ?? {},
|
|
290
|
+
subruleContent.push(await getRuleContent(subrule, subruleOptions ?? {}, context, onWarning));
|
|
287
291
|
} catch (error) {
|
|
288
|
-
|
|
292
|
+
const message = error instanceof Error ? error.cause instanceof Error ? error.cause.message : error.message : String(error);
|
|
293
|
+
onWarning?.(`Sub-rule ${String(index)} failed: ${message}`);
|
|
294
|
+
errors.push(error instanceof Error ? error : new Error(String(error)));
|
|
289
295
|
}
|
|
290
296
|
}
|
|
297
|
+
if (subruleContent.length === 0) throw new AggregateError(errors, "All sub-rules failed in compound rule");
|
|
291
298
|
return subruleContent.join("\n\n");
|
|
292
299
|
}
|
|
293
300
|
try {
|
|
294
|
-
return await rule.content(options,
|
|
301
|
+
return await rule.content(options, context);
|
|
295
302
|
} catch (error) {
|
|
296
|
-
if (check) throw error;
|
|
297
303
|
throw new Error("Failed to expand content", { cause: error });
|
|
298
304
|
}
|
|
299
305
|
}
|
|
@@ -340,6 +346,16 @@ function getSoleRecord(record) {
|
|
|
340
346
|
async function mdatExpand(tree, file, rules) {
|
|
341
347
|
validateRules(rules);
|
|
342
348
|
const normalizedRules = normalizeRules(rules);
|
|
349
|
+
const frontmatter = (() => {
|
|
350
|
+
if (typeof file.value !== "string") return;
|
|
351
|
+
const { data } = matter(file.value);
|
|
352
|
+
return Object.keys(data).length > 0 ? data : void 0;
|
|
353
|
+
})();
|
|
354
|
+
const context = {
|
|
355
|
+
filePath: file.history.length > 0 ? file.path : void 0,
|
|
356
|
+
frontmatter,
|
|
357
|
+
tree
|
|
358
|
+
};
|
|
343
359
|
const commentMarkers = [];
|
|
344
360
|
visit(tree, "html", (node, index, parent) => {
|
|
345
361
|
if (parent === void 0 || index === void 0) return CONTINUE;
|
|
@@ -357,7 +373,9 @@ async function mdatExpand(tree, file, rules) {
|
|
|
357
373
|
const rule = normalizedRules[keyword];
|
|
358
374
|
let newMarkdownString = "";
|
|
359
375
|
try {
|
|
360
|
-
newMarkdownString = await getRuleContent(rule, options,
|
|
376
|
+
newMarkdownString = await getRuleContent(rule, options, context, (warning) => {
|
|
377
|
+
saveLog(file, "warn", "expand", `${html}: ${warning}`, node);
|
|
378
|
+
});
|
|
361
379
|
if (newMarkdownString.trim() === "") saveLog(file, "error", "expand", `Got empty content when expanding ${html}`, node);
|
|
362
380
|
} catch (error) {
|
|
363
381
|
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.
|
|
3
|
+
"version": "2.0.0-preview.3",
|
|
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.
|
|
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
|
|
132
|
-
toc: (_options,
|
|
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
|
|