mdat 3.4.1 → 3.6.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/dist/bin/cli.js +1 -1
- package/dist/lib/index.d.ts +6 -0
- package/dist/lib/index.js +1 -1
- package/dist/{lib-CG4EOuoa.js → lib-CqQWGRqt.js} +401 -74
- package/package.json +12 -10
- package/readme.md +93 -21
- package/skills/mdat/SKILL.md +140 -0
- package/skills/mdat-plugin-authoring/SKILL.md +177 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mdat",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.6.0",
|
|
4
4
|
"description": "CLI tool and TypeScript library implementing the Markdown Autophagic Template (MDAT) system. MDAT lets you use comments as dynamic content templates in Markdown files, making it easy to generate and update readme boilerplate.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mdat",
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"mdat": "dist/bin/cli.js"
|
|
41
41
|
},
|
|
42
42
|
"files": [
|
|
43
|
-
"dist/*"
|
|
43
|
+
"dist/*",
|
|
44
|
+
"skills/*"
|
|
44
45
|
],
|
|
45
46
|
"dependencies": {
|
|
46
47
|
"@clack/prompts": "^1.8.1",
|
|
@@ -48,13 +49,14 @@
|
|
|
48
49
|
"cosmiconfig-typescript-loader": "^6.3.0",
|
|
49
50
|
"deepmerge-ts": "^8.0.2",
|
|
50
51
|
"globby": "^16.2.4",
|
|
52
|
+
"gray-matter-es": "^0.2.1",
|
|
51
53
|
"lognow": "^0.8.4",
|
|
52
54
|
"mdast-util-toc": "^7.1.0",
|
|
53
|
-
"metascope": "^0.12.
|
|
55
|
+
"metascope": "^0.12.1",
|
|
54
56
|
"path-type": "^6.0.0",
|
|
55
57
|
"picocolors": "^1.1.1",
|
|
56
58
|
"plur": "^6.0.0",
|
|
57
|
-
"pretty-bytes": "^7.
|
|
59
|
+
"pretty-bytes": "^7.2.0",
|
|
58
60
|
"pretty-ms": "^9.3.1",
|
|
59
61
|
"remark": "^15.0.1",
|
|
60
62
|
"remark-gfm": "^4.0.1",
|
|
@@ -69,26 +71,26 @@
|
|
|
69
71
|
},
|
|
70
72
|
"devDependencies": {
|
|
71
73
|
"@arethetypeswrong/core": "^0.18.5",
|
|
72
|
-
"@kitschpatrol/shared-config": "^8.
|
|
74
|
+
"@kitschpatrol/shared-config": "^8.11.0",
|
|
73
75
|
"@types/mdast": "^4.0.4",
|
|
74
76
|
"@types/node": "~24.13.6",
|
|
75
77
|
"@types/unist": "^3.0.3",
|
|
76
78
|
"@types/yargs": "^17.0.35",
|
|
77
|
-
"@vitest/coverage-v8": "^5.0.
|
|
79
|
+
"@vitest/coverage-v8": "^5.0.3",
|
|
78
80
|
"brewpub": "^0.4.0",
|
|
79
81
|
"bumpp": "^12.3.0",
|
|
80
82
|
"execa": "^10.0.1",
|
|
81
|
-
"mdat-plugin-cli-help": "^3.
|
|
83
|
+
"mdat-plugin-cli-help": "^3.4.0",
|
|
82
84
|
"mdat-plugin-example": "^3.0.0",
|
|
83
85
|
"mdat-plugin-tldraw": "^3.0.0",
|
|
84
86
|
"nanoid": "^6.0.1",
|
|
85
|
-
"prettier": "^3.9.
|
|
86
|
-
"publint": "^0.3.
|
|
87
|
+
"prettier": "^3.9.9",
|
|
88
|
+
"publint": "^0.3.25",
|
|
87
89
|
"shx": "^0.4.0",
|
|
88
90
|
"tsdown": "^0.23.0",
|
|
89
91
|
"typescript": "~6.0.3",
|
|
90
92
|
"unplugin-raw": "^0.8.0",
|
|
91
|
-
"vitest": "^5.0.
|
|
93
|
+
"vitest": "^5.0.3"
|
|
92
94
|
},
|
|
93
95
|
"peerDependencies": {
|
|
94
96
|
"prettier": "^3.0.0"
|
package/readme.md
CHANGED
|
@@ -4,22 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
<!-- /title -->
|
|
6
6
|
|
|
7
|
-
<!-- badges
|
|
8
|
-
custom: {
|
|
9
|
-
"Homebrew": {
|
|
10
|
-
image:
|
|
11
|
-
"https://img.shields.io/badge/Homebrew-kitschpatrol%2Ftap%2Fmdat-FBB040?logo=homebrew&logoColor=white",
|
|
12
|
-
link: "https://github.com/kitschpatrol/homebrew-tap/blob/main/Formula/mdat.rb",
|
|
13
|
-
},
|
|
14
|
-
}
|
|
15
|
-
}) -->
|
|
7
|
+
<!-- // badges -->
|
|
16
8
|
|
|
17
9
|
[](https://www.npmjs.com/package/mdat)
|
|
18
10
|
[](https://opensource.org/license/mit)
|
|
19
11
|
[](https://github.com/kitschpatrol/mdat/actions/workflows/ci.yml)
|
|
20
|
-
[](https://github.com/kitschpatrol/homebrew-tap/blob/
|
|
12
|
+
[](https://github.com/kitschpatrol/homebrew-tap/blob/HEAD/Formula/mdat.rb)
|
|
21
13
|
|
|
22
|
-
<!-- /badges -->
|
|
14
|
+
<!-- // /badges -->
|
|
23
15
|
|
|
24
16
|
<!-- description -->
|
|
25
17
|
|
|
@@ -46,6 +38,9 @@
|
|
|
46
38
|
- [Installing a rule plugin](#installing-a-rule-plugin)
|
|
47
39
|
- [Creating a rule plugin](#creating-a-rule-plugin)
|
|
48
40
|
- [Available rule plugins](#available-rule-plugins)
|
|
41
|
+
- [Agent skills](#agent-skills)
|
|
42
|
+
- [Skill: `mdat`](#skill-mdat)
|
|
43
|
+
- [Skill: `mdat-plugin-authoring`](#skill-mdat-plugin-authoring)
|
|
49
44
|
- [Migrating from 1.x to 2.x](#migrating-from-1x-to-2x)
|
|
50
45
|
- [Flat CLI commands](#flat-cli-commands)
|
|
51
46
|
- [Polyglot metadata](#polyglot-metadata)
|
|
@@ -124,7 +119,7 @@ Written in TypeScript with bundled type definitions.
|
|
|
124
119
|
|
|
125
120
|
### Installation
|
|
126
121
|
|
|
127
|
-
|
|
122
|
+
There are several ways to install mdat depending on how you're planning to use it:
|
|
128
123
|
|
|
129
124
|
#### CLI
|
|
130
125
|
|
|
@@ -247,7 +242,9 @@ npm install mdat
|
|
|
247
242
|
|
|
248
243
|
<!-- cli-help -->
|
|
249
244
|
|
|
250
|
-
####
|
|
245
|
+
#### Commands
|
|
246
|
+
|
|
247
|
+
##### Command: `mdat`
|
|
251
248
|
|
|
252
249
|
Work with MDAT placeholder comments in Markdown files.
|
|
253
250
|
|
|
@@ -271,7 +268,7 @@ mdat [command] [files..] [options]
|
|
|
271
268
|
|
|
272
269
|
_See the sections below for more information on each subcommand._
|
|
273
270
|
|
|
274
|
-
|
|
271
|
+
##### Subcommand: `mdat expand`
|
|
275
272
|
|
|
276
273
|
Expand MDAT placeholder comments. If no files are provided, the closest readme.md is expanded.
|
|
277
274
|
|
|
@@ -296,7 +293,7 @@ mdat expand [files..] [options]
|
|
|
296
293
|
| `--help`<br>`-h` | Show help | `boolean` | |
|
|
297
294
|
| `--version`<br>`-v` | Show version number | `boolean` | |
|
|
298
295
|
|
|
299
|
-
|
|
296
|
+
##### Subcommand: `mdat collapse`
|
|
300
297
|
|
|
301
298
|
Collapse MDAT placeholder comments. If no files are provided, the closest readme.md is collapsed.
|
|
302
299
|
|
|
@@ -320,7 +317,7 @@ mdat collapse [files..] [options]
|
|
|
320
317
|
| `--help`<br>`-h` | Show help | `boolean` | |
|
|
321
318
|
| `--version`<br>`-v` | Show version number | `boolean` | |
|
|
322
319
|
|
|
323
|
-
|
|
320
|
+
##### Subcommand: `mdat strip`
|
|
324
321
|
|
|
325
322
|
Strip MDAT comments while preserving expanded content. If no files are provided, the closest readme.md is stripped.
|
|
326
323
|
|
|
@@ -344,7 +341,7 @@ mdat strip [files..] [options]
|
|
|
344
341
|
| `--help`<br>`-h` | Show help | `boolean` | |
|
|
345
342
|
| `--version`<br>`-v` | Show version number | `boolean` | |
|
|
346
343
|
|
|
347
|
-
|
|
344
|
+
##### Subcommand: `mdat check`
|
|
348
345
|
|
|
349
346
|
Check if MDAT placeholder comments are up to date. Exits with code 1 if any files have stale or unexpanded content.
|
|
350
347
|
|
|
@@ -366,7 +363,7 @@ mdat check [files..] [options]
|
|
|
366
363
|
| `--help`<br>`-h` | Show help | `boolean` |
|
|
367
364
|
| `--version`<br>`-v` | Show version number | `boolean` |
|
|
368
365
|
|
|
369
|
-
|
|
366
|
+
##### Subcommand: `mdat create`
|
|
370
367
|
|
|
371
368
|
Create a new Markdown file from a template.
|
|
372
369
|
|
|
@@ -589,7 +586,17 @@ See the [Examples section](https://github.com/kitschpatrol/remark-mdat#examples)
|
|
|
589
586
|
|
|
590
587
|
### Bundled rules
|
|
591
588
|
|
|
592
|
-
Every rule that
|
|
589
|
+
Every stand-alone rule that generates a section of the readme emits its own heading, and accepts two options to control it:
|
|
590
|
+
|
|
591
|
+
- `heading` is `true` by default, which emits the rule's standard heading. Pass a string to replace the heading text, or `false` to leave the heading out, e.g. when you've written your own heading above the comment, or when the same rule appears several times in one section.
|
|
592
|
+
|
|
593
|
+
- `headingLevel` (`1`-`6`) sets the heading's Markdown level. Any sub-headings are nested one level deeper, whether or not the heading itself is shown. The defaults match each rule's placement in the bundled MDAT Readme template: `2` for top-level sections (`table-of-contents`, `contributing`, `license`, and `skills`), and `3` for the rules that sit under "Getting started" (`dependencies`, `install`, and `development-dependencies`).
|
|
594
|
+
|
|
595
|
+
```md
|
|
596
|
+
<!-- install({ heading: "Setup", headingLevel: 2 }) -->
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
Rules that generate inline content, like `badges`, `code`, and `size`, have no heading. Rule plugins follow the same convention.
|
|
593
600
|
|
|
594
601
|
#### Stand-alone
|
|
595
602
|
|
|
@@ -603,7 +610,28 @@ Every rule that emits a section heading accepts a `headingLevel` option (`1`-`6`
|
|
|
603
610
|
|
|
604
611
|
- ##### `<!-- badges -->`
|
|
605
612
|
|
|
606
|
-
Generates badges based on project metadata.
|
|
613
|
+
Generates badges based on project metadata. Each badge has an option. Leave it out for the default behavior, pass `false` to hide the badge, or pass `true` to require it, which reports an error if the metadata the badge needs is missing. Two options also take a value that replaces the detected metadata.
|
|
614
|
+
|
|
615
|
+
| Option | Badge | Default | Value |
|
|
616
|
+
| ------------------- | ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------- | ----------------------------------------------- |
|
|
617
|
+
| `npm` | NPM version, for each package | Shown for public packages | Package names (`string[]`), useful in monorepos |
|
|
618
|
+
| `license` | License | Shown when a license file matches a known SPDX license | |
|
|
619
|
+
| `ci` | GitHub Actions CI status | Shown when a workflow is named "CI" | |
|
|
620
|
+
| `gitLfs` | Git LFS | Shown when the repository tracks files with Git LFS | |
|
|
621
|
+
| `vscode` | VS Code Marketplace version | Shown for VS Code extensions hosted on GitHub | |
|
|
622
|
+
| `homebrew` | Homebrew tap | Shown when a `brewpub --tap` invocation is found in the package scripts | Formula (`"owner/tap/formula"`) |
|
|
623
|
+
| `githubRelease` | Latest GitHub release | Hidden | |
|
|
624
|
+
| `npmDownloads` | Monthly NPM downloads, for each package | Hidden | |
|
|
625
|
+
| `obsidianDownloads` | Obsidian community plugin downloads | Hidden | |
|
|
626
|
+
| `bundleSize` | [Bundlephobia](https://bundlephobia.com) minified and gzipped size, for each package | Hidden | |
|
|
627
|
+
|
|
628
|
+
Badges appear in the order of the table, followed by `custom` badges, which take a `Record<string, { image: string, link: string }>` keyed by alt text.
|
|
629
|
+
|
|
630
|
+
The download and size badges cover the packages listed in `npm`, or the current package if it's public, even when `npm` is `false`. VS Code extensions are identified by the `publisher` and `engines.vscode` fields in `package.json`. Shields.io retired its Marketplace badge, so the version is read from `package.json` in the GitHub repository instead, respecting `repository.directory` in monorepos. The Homebrew formula is detected the same way as in the `install` rule. The GitHub release badge needs a GitHub repository URL, and the Obsidian downloads badge reads the plugin ID from `manifest.json`.
|
|
631
|
+
|
|
632
|
+
```md
|
|
633
|
+
<!-- badges({ ci: false, npmDownloads: true }) -->
|
|
634
|
+
```
|
|
607
635
|
|
|
608
636
|
- ##### `<!-- description -->`
|
|
609
637
|
|
|
@@ -617,6 +645,8 @@ Every rule that emits a section heading accepts a `headingLevel` option (`1`-`6`
|
|
|
617
645
|
<!-- install({ headingLevel: 2, dev: true, homebrew: false }) -->
|
|
618
646
|
```
|
|
619
647
|
|
|
648
|
+
Also aliased as `<!-- installation -->`.
|
|
649
|
+
|
|
620
650
|
- ##### `<!-- dependencies -->`
|
|
621
651
|
|
|
622
652
|
Documents platform requirements and peer dependencies. Lists runtime platforms (Node, Python, Rust, Go, Ruby, etc.) with version constraints from `engines` or equivalent metadata, supported operating systems, and required peer dependencies with links to npm. Optional peer dependencies are left out. Version ranges are described in plain language (`>=24.16.0` becomes "24.16.0 or newer"), with the exact range included when it can't be summarized that simply. The optional `headingLevel` sets the Markdown heading level and defaults to `3`.
|
|
@@ -658,9 +688,13 @@ Every rule that emits a section heading accepts a `headingLevel` option (`1`-`6`
|
|
|
658
688
|
|
|
659
689
|
<!-- /size-table -->
|
|
660
690
|
|
|
691
|
+
- ##### `<!-- skills -->`
|
|
692
|
+
|
|
693
|
+
Documents the [Agent Skills](https://agentskills.io) bundled with the project and how to install them. Detects skills from `skills/<name>/SKILL.md` files, and gives each one a "Skill:" sub-heading linking to its `SKILL.md` file, followed by the first sentence of the description from its frontmatter. Publishable npm packages get instructions for syncing skills from the installed package with the `experimental_sync` command of Vercel's [skills CLI](https://github.com/vercel-labs/skills), and for installing them from the repository with `skills add`. Private packages and non-Node projects only get the repository instructions. Reports an error if no skills are found. Remember to include the `skills` directory in the `files` field of `package.json`. The optional `headingLevel` sets the Markdown heading level and defaults to `2`.
|
|
694
|
+
|
|
661
695
|
#### Compound
|
|
662
696
|
|
|
663
|
-
Compound rules combine several stand-alone rules under a single keyword. Options are passed as an array with one entry per stand-alone rule, in order, e.g. `<!-- footer([{ headingLevel: 3 }, { headingLevel: 3 }]) -->`.
|
|
697
|
+
Compound rules combine several stand-alone rules under a single keyword. Options are passed as an array with one entry per stand-alone rule, in order, e.g. `<!-- footer([{ headingLevel: 3 }, { heading: "Legal", headingLevel: 3 }]) -->`.
|
|
664
698
|
|
|
665
699
|
- ##### `<!-- header -->`
|
|
666
700
|
|
|
@@ -690,6 +724,8 @@ The `create` command provides starter readme templates:
|
|
|
690
724
|
- **Standard Readme basic** — Only the "required" sections from the [Standard Readme](https://github.com/RichardLitt/standard-readme/blob/main/spec.md) spec.
|
|
691
725
|
- **Standard Readme full** — All sections from the [Standard Readme](https://github.com/RichardLitt/standard-readme/blob/main/spec.md) spec.
|
|
692
726
|
|
|
727
|
+
If the project has agent skills in `skills/<name>/SKILL.md`, a `<!-- skills -->` placeholder is added after the template's "Usage" section.
|
|
728
|
+
|
|
693
729
|
## Plugins
|
|
694
730
|
|
|
695
731
|
Rule plugins are packages for sharing mdat expansion rules across projects.
|
|
@@ -756,6 +792,42 @@ Transform a CLI command's `--help` output into Markdown tables. Recursively call
|
|
|
756
792
|
|
|
757
793
|
Example: `<!-- cli-help -->`
|
|
758
794
|
|
|
795
|
+
#### [mdat-plugin-api-help](https://github.com/kitschpatrol/mdat-plugin-api-help)
|
|
796
|
+
|
|
797
|
+
Generate API documentation for a TypeScript package's public exports. Runs [TypeDoc](https://typedoc.org) over the package's entry point and embeds signatures, JSDoc descriptions, parameter and property tables, and examples as Markdown. A compact format renders one table row per export for large or namespaced APIs, and exports can be selected by name.
|
|
798
|
+
|
|
799
|
+
Example: `<!-- api-help({ format: "compact", include: ["sync*"] }) -->`
|
|
800
|
+
|
|
801
|
+
<!-- // skills -->
|
|
802
|
+
|
|
803
|
+
## Agent skills
|
|
804
|
+
|
|
805
|
+
This project bundles 2 [Agent Skills](https://agentskills.io) in its published package to help coding agents work with mdat.
|
|
806
|
+
|
|
807
|
+
To sync the skills into your project, run Vercel's [skills CLI](https://github.com/vercel-labs/skills) from your project root:
|
|
808
|
+
|
|
809
|
+
```sh
|
|
810
|
+
npx skills experimental_sync
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
Or install globally:
|
|
814
|
+
|
|
815
|
+
```sh
|
|
816
|
+
npx skills add kitschpatrol/mdat --global
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
Included skills:
|
|
820
|
+
|
|
821
|
+
### Skill: [`mdat`](skills/mdat/SKILL.md)
|
|
822
|
+
|
|
823
|
+
Use and configure mdat, the Markdown Autophagic Template CLI and library that expands HTML comment placeholders such as `<!-- title -->` in Markdown files.
|
|
824
|
+
|
|
825
|
+
### Skill: [`mdat-plugin-authoring`](skills/mdat-plugin-authoring/SKILL.md)
|
|
826
|
+
|
|
827
|
+
Write custom mdat expansion rules and publishable mdat rule plugins (mdat-plugin-\* packages).
|
|
828
|
+
|
|
829
|
+
<!-- // /skills -->
|
|
830
|
+
|
|
759
831
|
## Migrating from 1.x to 2.x
|
|
760
832
|
|
|
761
833
|
The 2.0 version introduces significant breaking changes in the interest of simplicity and a somewhat narrowed scope of concerns.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mdat
|
|
3
|
+
description: Use and configure mdat, the Markdown Autophagic Template CLI and library that expands HTML comment placeholders such as `<!-- title -->` in Markdown files. Use when a readme or other Markdown file contains mdat comment placeholders, when editing mdat.config.ts or an "mdat" key in package.json, when generating or updating readme boilerplate from project metadata, when `mdat check` fails, or when calling the mdat TypeScript API.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Using mdat
|
|
8
|
+
|
|
9
|
+
mdat expands HTML comments in Markdown files into generated content. A comment like `<!-- title -->` is a placeholder. Running `mdat` fills it in from a rule and adds a closing tag:
|
|
10
|
+
|
|
11
|
+
```md
|
|
12
|
+
<!-- title -->
|
|
13
|
+
|
|
14
|
+
# my-project
|
|
15
|
+
|
|
16
|
+
<!-- /title -->
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The comments stay in the file, so the content can be regenerated whenever the underlying data changes.
|
|
20
|
+
|
|
21
|
+
## Ground rules
|
|
22
|
+
|
|
23
|
+
- **Never hand-edit content between an opening tag and its closing tag.** It is overwritten on the next run. Change the comment's arguments, the rule, or the project metadata the rule reads from (e.g. `package.json`), then re-run `mdat`.
|
|
24
|
+
- **Text outside the tags is ordinary Markdown.** Edit it freely.
|
|
25
|
+
- **Re-run `mdat` after any change that affects generated content**, such as editing headings (the table of contents), `package.json` fields, or embedded files. Then confirm with `mdat check`.
|
|
26
|
+
- `mdat` rewrites files in place. Do not run it on files with uncommitted work you cannot recover.
|
|
27
|
+
- To add a placeholder, write only the opening comment. `mdat` adds the closing tag.
|
|
28
|
+
- Comments whose first word is not a known rule keyword are left alone, as are code-style comments like `<!-- // note -->`.
|
|
29
|
+
|
|
30
|
+
## CLI
|
|
31
|
+
|
|
32
|
+
If mdat is a project dependency, run it through the package manager (`pnpm mdat`, `npx mdat`). Run `mdat <command> --help` for the full option list.
|
|
33
|
+
|
|
34
|
+
| Command | Effect |
|
|
35
|
+
| ----------------------- | -------------------------------------------------------------------------------------- |
|
|
36
|
+
| `mdat` or `mdat expand` | Expand placeholders. With no files, expands the closest `readme.md`. |
|
|
37
|
+
| `mdat check` | Dry-run. Exits with code 1 if any file has stale or unexpanded content. Good for CI. |
|
|
38
|
+
| `mdat collapse` | Remove generated content, leaving only the opening comments. |
|
|
39
|
+
| `mdat strip` | Remove the mdat comments but keep the generated content. |
|
|
40
|
+
| `mdat create` | Create a new readme from a bundled template. Interactive unless `--interactive false`. |
|
|
41
|
+
|
|
42
|
+
Common options:
|
|
43
|
+
|
|
44
|
+
- `mdat docs/*.md` processes specific files.
|
|
45
|
+
- `--config a.ts b.json` (`-c`) loads additional rule files. Available on `expand` and `check`.
|
|
46
|
+
- `--format` (`-f`) formats output with Prettier, which must be installed. If the project formats Markdown with Prettier, pass `--format` to both `mdat` and `mdat check` so they agree.
|
|
47
|
+
- `--print` writes to stdout instead of the file.
|
|
48
|
+
- `--output` (`-o`) and `--name` (`-n`) write to a different directory or file name.
|
|
49
|
+
|
|
50
|
+
## Comment arguments
|
|
51
|
+
|
|
52
|
+
Arguments use function-call syntax with a single [JSON5](https://json5.org) value. Parentheses are required:
|
|
53
|
+
|
|
54
|
+
```md
|
|
55
|
+
<!-- install({ headingLevel: 2, dev: true }) -->
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Only JSON5 values are accepted. No JavaScript is evaluated. Bundled rules validate their arguments and report an error on unknown shapes.
|
|
59
|
+
|
|
60
|
+
## Bundled rules
|
|
61
|
+
|
|
62
|
+
These work without any configuration. They read normalized project metadata, so most also work in Python, Rust, Go, and Ruby projects, not only Node.
|
|
63
|
+
|
|
64
|
+
Rules that generate a section emit their own heading. Their `heading` option is `true` by default, takes a string to replace the heading text, and takes `false` to leave the heading out, e.g. under a hand-written heading or when a rule appears several times in one section. `headingLevel` sets the heading's level, with sub-headings nested one level deeper whether or not the heading is shown.
|
|
65
|
+
|
|
66
|
+
| Keyword | Output | Options |
|
|
67
|
+
| ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
68
|
+
| `title` | Project name as a heading. | |
|
|
69
|
+
| `banner` | A banner image found in the project directory. | |
|
|
70
|
+
| `badges` | npm version, license, CI, Git LFS, VS Code Marketplace, and Homebrew badges when detected, plus opt-in GitHub release, npm downloads, Obsidian downloads, and bundle size badges. | `npm`, `license`, `ci`, `gitLfs`, `vscode`, `homebrew`, `githubRelease`, `npmDownloads`, `obsidianDownloads`, `bundleSize`: `false` hides, `true` requires. Also `npm: string[]`, `homebrew: "owner/tap/formula"`, `custom: { [name]: { image, link } }` |
|
|
71
|
+
| `description` (alias `short-description`) | Project description. | |
|
|
72
|
+
| `table-of-contents` (alias `toc`) | Table of contents from the document headings. | `depth` (default `3`), `heading`, `headingLevel` (default `2`) |
|
|
73
|
+
| `install` (alias `installation`) | Install instructions for consumers of the package. Private packages are rejected. | `heading`, `headingLevel` (default `3`), `dev`, `homebrew` |
|
|
74
|
+
| `dependencies` | Runtime platform requirements and required peer dependencies. | `heading`, `headingLevel` (default `3`) |
|
|
75
|
+
| `development-dependencies` (alias `dev-dependencies`) | Tools needed to work on the project, from `devEngines` and `packageManager`. | `heading`, `headingLevel` (default `3`) |
|
|
76
|
+
| `contributing` | Invitation to open issues and pull requests. | `heading`, `headingLevel` (default `2`) |
|
|
77
|
+
| `license` | License section. | `heading`, `headingLevel` (default `2`) |
|
|
78
|
+
| `code` | A code block embedded from a file. | `file` (required), `language`, `trim` (default `true`) |
|
|
79
|
+
| `size` | A file's size. | `file` (required), `compression`: `none`, `gzip`, `brotli` |
|
|
80
|
+
| `size-table` | A table of file sizes with Gzip and Brotli columns. | `files` (required), `precision` (default `0`) |
|
|
81
|
+
| `skills` | Agent Skills found in `skills/<name>/SKILL.md` and how to install them. | `heading`, `headingLevel` (default `2`) |
|
|
82
|
+
| `header` | Compound: `title`, `banner`, `badges`, `short-description`. | Array, one entry per sub-rule in order. |
|
|
83
|
+
| `footer` | Compound: `contributing`, `license`. | Array, one entry per sub-rule in order. |
|
|
84
|
+
|
|
85
|
+
File paths in `code`, `size`, and `size-table` resolve from the current working directory.
|
|
86
|
+
|
|
87
|
+
## Configuration
|
|
88
|
+
|
|
89
|
+
Custom rules live in a config file found by [cosmiconfig](https://github.com/cosmiconfig/cosmiconfig). `mdat.config.ts` in the project root is the usual choice. The file default-exports a record whose keys become comment keywords:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { defineConfig } from 'mdat'
|
|
93
|
+
|
|
94
|
+
export default defineConfig({
|
|
95
|
+
// Object form: adds processing order
|
|
96
|
+
date: {
|
|
97
|
+
content: () => new Date().toISOString(),
|
|
98
|
+
order: 1,
|
|
99
|
+
},
|
|
100
|
+
// String: static replacement
|
|
101
|
+
greeting: 'Hello, world!',
|
|
102
|
+
// Function: sync or async, receives the comment's arguments, e.g. <!-- repeat(3) -->
|
|
103
|
+
repeat: (options) => 'ha'.repeat(Number(options)),
|
|
104
|
+
})
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
- A rule must return non-empty Markdown. Empty content and thrown errors are reported as errors.
|
|
108
|
+
- Custom rules merge with the bundled rules. A custom rule with the same keyword as a bundled rule replaces it.
|
|
109
|
+
- With multiple config files, the last rule for a keyword wins, and `--config` files take precedence over discovered config.
|
|
110
|
+
- JSON files work as rule sets. Nested keys are flattened into dot-notated keywords, so `{ "a": { "b": "x" } }` provides `<!-- a.b -->`.
|
|
111
|
+
- `package.json` can hold rules under an `"mdat"` key, or name a shared config package: `"mdat": "@scope/mdat-config"`.
|
|
112
|
+
- Rules from a plugin package are spread into the config: `defineConfig({ ...examplePlugin })`.
|
|
113
|
+
|
|
114
|
+
For rules that take validated arguments, read the document tree, or are shared across projects, use the `mdat-plugin-authoring` skill if it is available.
|
|
115
|
+
|
|
116
|
+
## Library API
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
import { check, expand, expandString } from 'mdat'
|
|
120
|
+
import { write } from 'to-vfile'
|
|
121
|
+
|
|
122
|
+
// Returns VFiles. Writing is the caller's responsibility.
|
|
123
|
+
const [file] = await expand('readme.md')
|
|
124
|
+
await write(file)
|
|
125
|
+
|
|
126
|
+
const result = await expandString('<!-- title -->')
|
|
127
|
+
console.log(result.toString())
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
- `expand`, `collapse`, `strip`, and `check` take file paths. With no files they find the closest readme.
|
|
131
|
+
- `expandString`, `collapseString`, `stripString`, and `checkString` take Markdown strings.
|
|
132
|
+
- `create` and `createInteractive` create a readme from a bundled template.
|
|
133
|
+
- `loadConfig` discovers and merges config. `defineConfig` and `mergeConfig` build config objects.
|
|
134
|
+
|
|
135
|
+
## Troubleshooting
|
|
136
|
+
|
|
137
|
+
- **A comment is not expanding.** The keyword does not match a loaded rule. Check spelling, confirm the config file is discovered or passed with `--config`, and run with `--verbose`.
|
|
138
|
+
- **`mdat check` fails.** Run `mdat` (with `--format` if the check used it) and commit the result. If it still fails, the rule output is non-deterministic, e.g. a timestamp.
|
|
139
|
+
- **Arguments are ignored or rejected.** Use parentheses: `<!-- keyword({ key: "value" }) -->`. The 1.x form without parentheses is no longer supported.
|
|
140
|
+
- **Old commands.** `mdat readme` is now `mdat`, `mdat readme init` is `mdat create`, and `mdat readme check` is `mdat check`. The `--rules`, `--assets`, `--package`, `--meta`, and `--prefix` options were removed in favor of `--config`.
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mdat-plugin-authoring
|
|
3
|
+
description: Write custom mdat expansion rules and publishable mdat rule plugins (mdat-plugin-* packages). Use when creating or debugging rule functions for mdat comment placeholders, validating comment arguments, reading project metadata or the Markdown tree inside a rule, building compound rules, or packaging and testing rules for reuse across projects.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Authoring mdat rules and plugins
|
|
8
|
+
|
|
9
|
+
An mdat rule maps a comment keyword to generated Markdown. A rule plugin is an npm package whose default export is a record of rules. Start with a rule in the project's `mdat.config.ts`. Move it to a plugin package only when more than one project needs it.
|
|
10
|
+
|
|
11
|
+
## Rule shapes
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import type { Config } from 'mdat'
|
|
15
|
+
|
|
16
|
+
export default {
|
|
17
|
+
// String: static replacement for <!-- greeting -->
|
|
18
|
+
greeting: 'Hello, world!',
|
|
19
|
+
// Array: compound rule, each entry is itself a rule
|
|
20
|
+
intro: ['# My Project', () => 'Generated description.'],
|
|
21
|
+
// Object: adds processing order
|
|
22
|
+
summary: {
|
|
23
|
+
content: (_options, context) => `Source: ${context.filePath ?? 'unknown'}`,
|
|
24
|
+
order: 1,
|
|
25
|
+
},
|
|
26
|
+
// Function: sync or async
|
|
27
|
+
time: () => new Date().toDateString(),
|
|
28
|
+
} satisfies Config
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The full signature of a content function:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
type Content = (options: JsonValue, context: RuleContext) => Promise<string> | string
|
|
35
|
+
|
|
36
|
+
type RuleContext = {
|
|
37
|
+
/** File path of the source document, if known. */
|
|
38
|
+
filePath: string | undefined
|
|
39
|
+
/** Parsed YAML frontmatter from the document, if present. */
|
|
40
|
+
frontmatter: Record<string, unknown> | undefined
|
|
41
|
+
/** The full mdast tree of the document. Do not mutate. */
|
|
42
|
+
tree: Root
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Requirements for a rule
|
|
47
|
+
|
|
48
|
+
- **Return a non-empty Markdown string.** Empty output is reported as an error. If there is nothing to output, throw an error that says why.
|
|
49
|
+
- **Be deterministic.** `mdat check` compares a fresh expansion against the file on disk, so timestamps, random values, and unstable ordering make the check fail on every run.
|
|
50
|
+
- **Fail with a clear message.** Thrown errors are reported against the comment that triggered them. Include what was missing and how to fix it.
|
|
51
|
+
- **Resolve relative paths deliberately.** The bundled rules resolve file arguments from `process.cwd()`. Use `context.filePath` to resolve relative to the document instead.
|
|
52
|
+
- **Do not include the comment tags in the output.** mdat adds the opening and closing tags.
|
|
53
|
+
|
|
54
|
+
## Keywords
|
|
55
|
+
|
|
56
|
+
A keyword consists of letters, numbers, `_`, `$`, `-`, and `.`, and cannot start with `/`, `*`, `#`, `-`, or `.`. Prefer kebab-case, e.g. `cli-help`. Pick a specific keyword. A rule with the same keyword as a bundled rule (`title`, `badges`, `install`, `license`, and so on) replaces it.
|
|
57
|
+
|
|
58
|
+
## Arguments
|
|
59
|
+
|
|
60
|
+
A comment passes a single JSON5 value: `<!-- keyword({ file: "./a.ts", trim: false }) -->`. A comment without parentheses passes `{}`. Arguments are untrusted input, so validate them. The bundled rules use [Zod](https://zod.dev):
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import type { Config } from 'mdat'
|
|
64
|
+
import fs from 'node:fs/promises'
|
|
65
|
+
import path from 'node:path'
|
|
66
|
+
import { z } from 'zod'
|
|
67
|
+
|
|
68
|
+
const optionsSchema = z.object({
|
|
69
|
+
file: z.string(),
|
|
70
|
+
language: z.string().optional(),
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
export default {
|
|
74
|
+
embed: {
|
|
75
|
+
async content(options) {
|
|
76
|
+
const { file, language } = optionsSchema.parse(options)
|
|
77
|
+
const code = await fs.readFile(path.join(process.cwd(), file), 'utf8')
|
|
78
|
+
return `\`\`\`${language ?? path.extname(file).slice(1)}\n${code.trim()}\n\`\`\``
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
} satisfies Config
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Prefer an object argument over a bare primitive so options can be added later without breaking existing comments.
|
|
85
|
+
|
|
86
|
+
## Project metadata
|
|
87
|
+
|
|
88
|
+
mdat exports helpers so rules do not need to parse `package.json` themselves. Both are memoized and work across ecosystems (Node, Python, Rust, Go, Ruby) via [metascope](https://github.com/kitschpatrol/metascope):
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
import { getContextMetadata, getReadmeMetadata } from 'mdat'
|
|
92
|
+
|
|
93
|
+
// Normalized fields the bundled readme rules use, e.g. name, description,
|
|
94
|
+
// repositoryUrl, issuesUrl, license, author, bin, engines, peerDependencies.
|
|
95
|
+
// Any field may be undefined.
|
|
96
|
+
const { name, repositoryUrl } = await getReadmeMetadata()
|
|
97
|
+
|
|
98
|
+
// The full metascope metadata context for anything not covered above.
|
|
99
|
+
const metadata = await getContextMetadata()
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
See the exported `ReadmeMetadata` type for the complete field list. Call `resetMetadataCaches()` between tests that change project files on disk.
|
|
103
|
+
|
|
104
|
+
## Headings
|
|
105
|
+
|
|
106
|
+
Follow the convention of the bundled rules so that readmes stay consistent:
|
|
107
|
+
|
|
108
|
+
- A rule that generates a section emits its own heading. A rule that generates inline content, like a badge or a code block, has no heading.
|
|
109
|
+
- Accept a `heading` option. `true` is the default and emits the rule's standard heading, a string replaces the heading text, and `false` leaves the heading out.
|
|
110
|
+
- Accept a `headingLevel` option (`1`-`6`) for the heading's level. Nest any sub-headings one level deeper, whether or not the heading is shown. Default to the level that suits the section's usual place in a readme.
|
|
111
|
+
|
|
112
|
+
## Order and compound rules
|
|
113
|
+
|
|
114
|
+
- `order` defaults to `0`. Rules with a higher order run later. Use it when a rule reads content other rules generate. The bundled `table-of-contents` rule uses `order: 1` so that it sees headings produced by other rules.
|
|
115
|
+
- A compound rule is an array of rules whose output is joined under one keyword. Its comment takes an array of arguments, one entry per sub-rule in order: `<!-- footer([{ headingLevel: 3 }, { headingLevel: 3 }]) -->`.
|
|
116
|
+
- To reuse a rule from another single-rule record inside a compound rule, use `getSoleRule` from `remark-mdat`.
|
|
117
|
+
|
|
118
|
+
## Packaging a plugin
|
|
119
|
+
|
|
120
|
+
- Name the package with the `mdat-plugin-` prefix and add the `mdat-plugin` keyword.
|
|
121
|
+
- Publish ESM with a default export of the rule record, typed with `satisfies Config`.
|
|
122
|
+
- Declare `mdat` as a peer dependency, and as a dev dependency for tests.
|
|
123
|
+
- One package may export several related rules.
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"name": "mdat-plugin-example",
|
|
128
|
+
"type": "module",
|
|
129
|
+
"exports": {
|
|
130
|
+
".": {
|
|
131
|
+
"types": "./dist/index.d.ts",
|
|
132
|
+
"import": "./dist/index.js"
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
"keywords": ["mdat", "mdat-plugin"],
|
|
136
|
+
"peerDependencies": {
|
|
137
|
+
"mdat": "^3.0.0"
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Consumers spread the plugin into their config:
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import { defineConfig } from 'mdat'
|
|
146
|
+
import example from 'mdat-plugin-example'
|
|
147
|
+
|
|
148
|
+
export default defineConfig({ ...example })
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
[mdat-plugin-example](https://github.com/kitschpatrol/mdat-plugin-example) is a minimal template repository. [mdat-plugin-cli-help](https://github.com/kitschpatrol/mdat-plugin-cli-help) and [mdat-plugin-tldraw](https://github.com/kitschpatrol/mdat-plugin-tldraw) are fuller examples with arguments and file output.
|
|
152
|
+
|
|
153
|
+
## Testing
|
|
154
|
+
|
|
155
|
+
Test rules through the public API with `expandString`, passing the rule record as config:
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
import { expandString } from 'mdat'
|
|
159
|
+
import { expect, it } from 'vitest'
|
|
160
|
+
import plugin from '../src/index'
|
|
161
|
+
|
|
162
|
+
it('expands the example rule', async () => {
|
|
163
|
+
const result = await expandString('<!-- example -->', plugin)
|
|
164
|
+
expect(result.toString()).toContain('Hello from')
|
|
165
|
+
expect(result.messages.filter((message) => message.fatal)).toHaveLength(0)
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it('is stable across runs', async () => {
|
|
169
|
+
const first = await expandString('<!-- example -->', plugin)
|
|
170
|
+
const second = await expandString(first.toString(), plugin)
|
|
171
|
+
expect(second.toString()).toEqual(first.toString())
|
|
172
|
+
})
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Also cover invalid arguments. Rule errors are not thrown. They are reported as messages on the returned VFile with `fatal: true`, alongside informational messages for each successful expansion, so assert on the fatal messages in `result.messages`.
|
|
176
|
+
|
|
177
|
+
To try a rule file against a real document without touching it, run `mdat --config ./my-rules.ts --print some-file.md`.
|