mdat 2.0.1 → 2.2.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/.DS_Store +0 -0
- package/dist/bin/cli.js +82 -27
- package/dist/lib/index.d.ts +42 -12
- package/dist/lib/index.js +93 -30
- package/package.json +7 -5
- package/readme.md +46 -55
package/dist/.DS_Store
CHANGED
|
Binary file
|
package/dist/bin/cli.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import picocolors from "picocolors";
|
|
3
3
|
import prettyMilliseconds from "pretty-ms";
|
|
4
|
-
import { getMdatReports, getSoleRule,
|
|
4
|
+
import { getMdatReports, getSoleRule, mdatCollapse, mdatDiff, mdatExpand, mdatSplit, mdatStrip, reporterMdat, rulesSchema, setLogger } from "remark-mdat";
|
|
5
|
+
import { remark } from "remark";
|
|
6
|
+
import remarkGfm from "remark-gfm";
|
|
5
7
|
import { cosmiconfig, defaultLoaders } from "cosmiconfig";
|
|
6
8
|
import { TypeScriptLoader } from "cosmiconfig-typescript-loader";
|
|
7
9
|
import fs from "node:fs/promises";
|
|
@@ -17,8 +19,6 @@ import { promisify } from "node:util";
|
|
|
17
19
|
import { brotliCompress, gzip } from "node:zlib";
|
|
18
20
|
import prettyBytes from "pretty-bytes";
|
|
19
21
|
import { toc } from "mdast-util-toc";
|
|
20
|
-
import { remark } from "remark";
|
|
21
|
-
import remarkGfm from "remark-gfm";
|
|
22
22
|
import { read, write } from "to-vfile";
|
|
23
23
|
import "vfile";
|
|
24
24
|
import { Configuration } from "unified-engine";
|
|
@@ -42,7 +42,7 @@ function deepMergeDefined(...objects) {
|
|
|
42
42
|
//#endregion
|
|
43
43
|
//#region package.json
|
|
44
44
|
var name = "mdat";
|
|
45
|
-
var version = "2.0
|
|
45
|
+
var version = "2.2.0";
|
|
46
46
|
|
|
47
47
|
//#endregion
|
|
48
48
|
//#region src/lib/log.ts
|
|
@@ -603,7 +603,7 @@ async function formatWithPrettier(content, filePath) {
|
|
|
603
603
|
if (config === void 0 && !configCache.has(configKey)) {
|
|
604
604
|
config = await cachedPrettier.resolveConfig(filePath ?? process.cwd());
|
|
605
605
|
configCache.set(configKey, config);
|
|
606
|
-
if (config) log.debug(`
|
|
606
|
+
if (config) log.debug(`Resolved Prettier config for "${configKey}"`);
|
|
607
607
|
}
|
|
608
608
|
return cachedPrettier.format(content, {
|
|
609
609
|
...config,
|
|
@@ -662,7 +662,8 @@ async function findReadme() {
|
|
|
662
662
|
}
|
|
663
663
|
/**
|
|
664
664
|
* Searches up for a readme.md file.
|
|
665
|
-
*
|
|
665
|
+
*
|
|
666
|
+
* @throws {Error} If no readme is found
|
|
666
667
|
*/
|
|
667
668
|
async function findReadmeThrows() {
|
|
668
669
|
const readme = await findReadme();
|
|
@@ -740,17 +741,26 @@ function getExpandProcessor(config, ambientRemarkConfig) {
|
|
|
740
741
|
emphasis: "_"
|
|
741
742
|
} }).use(remarkGfm).use(ambientRemarkConfig).use(() => async function(tree, file) {
|
|
742
743
|
mdatSplit(tree, file);
|
|
743
|
-
|
|
744
|
+
mdatCollapse(tree, file);
|
|
744
745
|
await mdatExpand(tree, file, config);
|
|
745
746
|
});
|
|
746
747
|
}
|
|
747
|
-
function
|
|
748
|
+
function getCollapseProcessor(_config, ambientRemarkConfig) {
|
|
748
749
|
return remark().use({ settings: {
|
|
749
750
|
bullet: "-",
|
|
750
751
|
emphasis: "_"
|
|
751
752
|
} }).use(remarkGfm).use(ambientRemarkConfig).use(() => function(tree, file) {
|
|
752
753
|
mdatSplit(tree, file);
|
|
753
|
-
|
|
754
|
+
mdatCollapse(tree, file);
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
function getStripProcessor(_config, ambientRemarkConfig) {
|
|
758
|
+
return remark().use({ settings: {
|
|
759
|
+
bullet: "-",
|
|
760
|
+
emphasis: "_"
|
|
761
|
+
} }).use(remarkGfm).use(ambientRemarkConfig).use(() => function(tree, file) {
|
|
762
|
+
mdatSplit(tree, file);
|
|
763
|
+
mdatStrip(tree, file);
|
|
754
764
|
});
|
|
755
765
|
}
|
|
756
766
|
|
|
@@ -874,9 +884,8 @@ async function createReadme(options) {
|
|
|
874
884
|
return readmePath;
|
|
875
885
|
}
|
|
876
886
|
function getTemplateForConfig(templateKey, compound) {
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
return templateString;
|
|
887
|
+
if (!(templateKey in templates_default)) throw new Error(`Unknown template "${templateKey}". Available templates: ${Object.keys(templates_default).join(", ")}`);
|
|
888
|
+
return templates_default[templateKey].content[compound ? "compound" : "explicit"];
|
|
880
889
|
}
|
|
881
890
|
function getTemplateOptions() {
|
|
882
891
|
return Object.entries(templates_default).map(([key, value]) => ({
|
|
@@ -889,10 +898,11 @@ function getTemplateOptions() {
|
|
|
889
898
|
//#endregion
|
|
890
899
|
//#region src/lib/api.ts
|
|
891
900
|
/**
|
|
892
|
-
* Expand MDAT comments in one or more Markdown files.
|
|
893
|
-
*
|
|
894
|
-
*
|
|
895
|
-
*
|
|
901
|
+
* Expand MDAT comments in one or more Markdown files. If no files are provided,
|
|
902
|
+
* auto-finds the closest readme.md. Writing is the responsibility of the caller
|
|
903
|
+
* (e.g. via `await write(result)`)
|
|
904
|
+
*
|
|
905
|
+
* @returns An array of VFiles
|
|
896
906
|
*/
|
|
897
907
|
async function expand(files, name, output, config, options) {
|
|
898
908
|
files ??= await findReadmeThrows();
|
|
@@ -901,21 +911,37 @@ async function expand(files, name, output, config, options) {
|
|
|
901
911
|
return results;
|
|
902
912
|
}
|
|
903
913
|
/**
|
|
904
|
-
* Collapse MDAT comments in one or more Markdown files.
|
|
905
|
-
*
|
|
906
|
-
*
|
|
907
|
-
*
|
|
914
|
+
* Collapse MDAT comments in one or more Markdown files. If no files are
|
|
915
|
+
* provided, auto-finds the closest readme.md. Writing is the responsibility of
|
|
916
|
+
* the caller (e.g. via `await write(result)`)
|
|
917
|
+
*
|
|
918
|
+
* @returns An array of VFiles
|
|
908
919
|
*/
|
|
909
920
|
async function collapse(files, name, output, config, options) {
|
|
910
921
|
files ??= await findReadmeThrows();
|
|
911
|
-
const results = await processFiles(files, loadConfig,
|
|
922
|
+
const results = await processFiles(files, loadConfig, getCollapseProcessor, name, output, config);
|
|
912
923
|
if (options?.format) await formatResults(results);
|
|
913
924
|
return results;
|
|
914
925
|
}
|
|
915
926
|
/**
|
|
916
|
-
*
|
|
917
|
-
*
|
|
918
|
-
*
|
|
927
|
+
* Strips MDAT comments in one or more Markdown files without touching other
|
|
928
|
+
* content. Does _not_ automatically expand Mdat content before stripping the
|
|
929
|
+
* tags. If no files are provided, auto-finds the closest readme.md. Writing is
|
|
930
|
+
* the responsibility of the caller (e.g. via `await write(result)`)
|
|
931
|
+
*
|
|
932
|
+
* @returns An array of VFiles
|
|
933
|
+
*/
|
|
934
|
+
async function strip(files, name, output, config, options) {
|
|
935
|
+
files ??= await findReadmeThrows();
|
|
936
|
+
const results = await processFiles(files, loadConfig, getStripProcessor, name, output, config);
|
|
937
|
+
if (options?.format) await formatResults(results);
|
|
938
|
+
return results;
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Dry-run expand and compare with file on disk. If no files are provided,
|
|
942
|
+
* auto-finds the closest readme.md.
|
|
943
|
+
*
|
|
944
|
+
* @returns Per-file sync status and expanded VFiles
|
|
919
945
|
*/
|
|
920
946
|
async function check(files, config, options) {
|
|
921
947
|
files ??= await findReadmeThrows();
|
|
@@ -923,10 +949,26 @@ async function check(files, config, options) {
|
|
|
923
949
|
const resolvedFiles = Array.isArray(files) ? files : [files];
|
|
924
950
|
const [originals, results] = await Promise.all([Promise.all(resolvedFiles.map(async (f) => read(f))), processFiles(files, loadConfig, getExpandProcessor, void 0, void 0, config)]);
|
|
925
951
|
if (options?.format) await formatResults(results);
|
|
926
|
-
return results.map((result, i) => (
|
|
927
|
-
|
|
952
|
+
return results.map((result, i) => compareWithDiff(originals[i], result, options));
|
|
953
|
+
}
|
|
954
|
+
function compareWithDiff(original, result, options) {
|
|
955
|
+
const inSync = original.toString().replaceAll("\r\n", "\n") === result.toString();
|
|
956
|
+
if (!inSync) {
|
|
957
|
+
const parser = remark().use(remarkGfm);
|
|
958
|
+
const originalTree = parser.parse(original.toString());
|
|
959
|
+
mdatSplit(originalTree, original);
|
|
960
|
+
const expandedTree = parser.parse(result.toString());
|
|
961
|
+
mdatSplit(expandedTree, result);
|
|
962
|
+
const diffResults = mdatDiff(originalTree, original, expandedTree, result);
|
|
963
|
+
if (options?.format && diffResults.every((r) => r.status === "ok")) {
|
|
964
|
+
const message = result.message("Formatting differences outside mdat tags", { source: "diff" });
|
|
965
|
+
message.fatal = false;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
return {
|
|
969
|
+
inSync,
|
|
928
970
|
result
|
|
929
|
-
}
|
|
971
|
+
};
|
|
930
972
|
}
|
|
931
973
|
async function formatResults(results) {
|
|
932
974
|
for (const file of results) file.value = await formatWithPrettier(file.toString(), file.path || void 0);
|
|
@@ -1041,6 +1083,18 @@ try {
|
|
|
1041
1083
|
reporterMdat(results);
|
|
1042
1084
|
log.debug(`Collapsed comments in ${prettyMilliseconds(performance.now() - startTime)}.`);
|
|
1043
1085
|
process.exitCode = getExitCode(results);
|
|
1086
|
+
}).command("strip [files..] [options]", "Strip MDAT comments while preserving expanded content. If no files are provided, the closest readme.md is stripped.", (yargs) => yargs.positional(...filesPositional).option(outputOption).option(nameOption).option(printOption).option(formatOption), async ({ files, format, name, output, print }) => {
|
|
1087
|
+
logConflicts({
|
|
1088
|
+
name,
|
|
1089
|
+
output,
|
|
1090
|
+
print
|
|
1091
|
+
});
|
|
1092
|
+
const results = await strip(files, name, output, void 0, { format });
|
|
1093
|
+
for (const file of results) if (print) process.stdout.write(file.toString());
|
|
1094
|
+
else await write(file);
|
|
1095
|
+
reporterMdat(results);
|
|
1096
|
+
log.debug(`Stripped comments in ${prettyMilliseconds(performance.now() - startTime)}.`);
|
|
1097
|
+
process.exitCode = getExitCode(results);
|
|
1044
1098
|
}).command("check [files..] [options]", "Check if MDAT placeholder comments are up to date. Exits with code 1 if any files have stale or unexpanded content.", (yargs) => yargs.positional(...filesPositional).option(configOption).option(formatOption), async ({ config, files, format }) => {
|
|
1045
1099
|
const results = await check(files, collectConfig(config), { format });
|
|
1046
1100
|
let allInSync = true;
|
|
@@ -1049,6 +1103,7 @@ try {
|
|
|
1049
1103
|
if (inSync) log.debug(`${picocolors.green("Up to date")}: ${filePath}`);
|
|
1050
1104
|
else {
|
|
1051
1105
|
log.warn(`${picocolors.red("Stale content")}: ${filePath}`);
|
|
1106
|
+
reporterMdat([result]);
|
|
1052
1107
|
allInSync = false;
|
|
1053
1108
|
}
|
|
1054
1109
|
}
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -67,10 +67,11 @@ declare function createReadme(options?: Partial<MdatReadmeCreateOptions>): Promi
|
|
|
67
67
|
//#endregion
|
|
68
68
|
//#region src/lib/api.d.ts
|
|
69
69
|
/**
|
|
70
|
-
* Expand MDAT comments in one or more Markdown files.
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
70
|
+
* Expand MDAT comments in one or more Markdown files. If no files are provided,
|
|
71
|
+
* auto-finds the closest readme.md. Writing is the responsibility of the caller
|
|
72
|
+
* (e.g. via `await write(result)`)
|
|
73
|
+
*
|
|
74
|
+
* @returns An array of VFiles
|
|
74
75
|
*/
|
|
75
76
|
declare function expand(files?: string | string[], name?: string, output?: string, config?: ConfigToLoad, options?: {
|
|
76
77
|
format?: boolean;
|
|
@@ -82,10 +83,11 @@ declare function expandString(markdown: string, config?: ConfigToLoad, options?:
|
|
|
82
83
|
format?: boolean;
|
|
83
84
|
}): Promise<VFile>;
|
|
84
85
|
/**
|
|
85
|
-
* Collapse MDAT comments in one or more Markdown files.
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
86
|
+
* Collapse MDAT comments in one or more Markdown files. If no files are
|
|
87
|
+
* provided, auto-finds the closest readme.md. Writing is the responsibility of
|
|
88
|
+
* the caller (e.g. via `await write(result)`)
|
|
89
|
+
*
|
|
90
|
+
* @returns An array of VFiles
|
|
89
91
|
*/
|
|
90
92
|
declare function collapse(files?: string | string[], name?: string, output?: string, config?: ConfigToLoad, options?: {
|
|
91
93
|
format?: boolean;
|
|
@@ -97,9 +99,27 @@ declare function collapseString(markdown: string, config?: ConfigToLoad, options
|
|
|
97
99
|
format?: boolean;
|
|
98
100
|
}): Promise<VFile>;
|
|
99
101
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
102
|
+
* Strips MDAT comments in one or more Markdown files without touching other
|
|
103
|
+
* content. Does _not_ automatically expand Mdat content before stripping the
|
|
104
|
+
* tags. If no files are provided, auto-finds the closest readme.md. Writing is
|
|
105
|
+
* the responsibility of the caller (e.g. via `await write(result)`)
|
|
106
|
+
*
|
|
107
|
+
* @returns An array of VFiles
|
|
108
|
+
*/
|
|
109
|
+
declare function strip(files?: string | string[], name?: string, output?: string, config?: ConfigToLoad, options?: {
|
|
110
|
+
format?: boolean;
|
|
111
|
+
}): Promise<VFile[]>;
|
|
112
|
+
/**
|
|
113
|
+
* Strip MDAT comments from a Markdown string.
|
|
114
|
+
*/
|
|
115
|
+
declare function stripString(markdown: string, config?: ConfigToLoad, options?: {
|
|
116
|
+
format?: boolean;
|
|
117
|
+
}): Promise<VFile>;
|
|
118
|
+
/**
|
|
119
|
+
* Dry-run expand and compare with file on disk. If no files are provided,
|
|
120
|
+
* auto-finds the closest readme.md.
|
|
121
|
+
*
|
|
122
|
+
* @returns Per-file sync status and expanded VFiles
|
|
103
123
|
*/
|
|
104
124
|
declare function check(files?: string | string[], config?: ConfigToLoad, options?: {
|
|
105
125
|
format?: boolean;
|
|
@@ -107,6 +127,16 @@ declare function check(files?: string | string[], config?: ConfigToLoad, options
|
|
|
107
127
|
inSync: boolean;
|
|
108
128
|
result: VFile;
|
|
109
129
|
}>>;
|
|
130
|
+
/**
|
|
131
|
+
* Check if MDAT comments in a Markdown string are up to date by expanding and
|
|
132
|
+
* comparing per-tag.
|
|
133
|
+
*/
|
|
134
|
+
declare function checkString(markdown: string, config?: ConfigToLoad, options?: {
|
|
135
|
+
format?: boolean;
|
|
136
|
+
}): Promise<{
|
|
137
|
+
inSync: boolean;
|
|
138
|
+
result: VFile;
|
|
139
|
+
}>;
|
|
110
140
|
//#endregion
|
|
111
141
|
//#region src/lib/context.d.ts
|
|
112
142
|
/**
|
|
@@ -160,4 +190,4 @@ declare function resetMetadataCaches(): void;
|
|
|
160
190
|
*/
|
|
161
191
|
declare function setLogger(logger?: ILogBasic | ILogLayer): void;
|
|
162
192
|
//#endregion
|
|
163
|
-
export { type Config, type ReadmeMetadata, type Rule, check, collapse, collapseString, createReadme as create, createReadmeInteractive as createInteractive, defineConfig, expand, expandString, getContextMetadata, getReadmeMetadata, loadConfig, mergeConfig, resetMetadataCaches, setLogger };
|
|
193
|
+
export { type Config, type ReadmeMetadata, type Rule, check, checkString, collapse, collapseString, createReadme as create, createReadmeInteractive as createInteractive, defineConfig, expand, expandString, getContextMetadata, getReadmeMetadata, loadConfig, mergeConfig, resetMetadataCaches, setLogger, strip, stripString };
|
package/dist/lib/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { remark } from "remark";
|
|
2
|
+
import remarkGfm from "remark-gfm";
|
|
3
|
+
import { getSoleRule, mdatCollapse, mdatDiff, mdatExpand, mdatSplit, mdatStrip, rulesSchema, setLogger as setLogger$1 } from "remark-mdat";
|
|
1
4
|
import { cosmiconfig, defaultLoaders } from "cosmiconfig";
|
|
2
5
|
import { TypeScriptLoader } from "cosmiconfig-typescript-loader";
|
|
3
6
|
import fs from "node:fs/promises";
|
|
4
7
|
import path from "node:path";
|
|
5
8
|
import picocolors from "picocolors";
|
|
6
9
|
import plur from "plur";
|
|
7
|
-
import { getSoleRule, mdatClean, mdatExpand, mdatSplit, rulesSchema, setLogger as setLogger$1 } from "remark-mdat";
|
|
8
10
|
import { deepmerge } from "deepmerge-ts";
|
|
9
11
|
import { createLogger, getChildLogger, injectionHelper } from "lognow";
|
|
10
12
|
import { defineTemplate, getMetadata, helpers, setLogger as setLogger$2, templates } from "metascope";
|
|
@@ -15,8 +17,6 @@ import { promisify } from "node:util";
|
|
|
15
17
|
import { brotliCompress, gzip } from "node:zlib";
|
|
16
18
|
import prettyBytes from "pretty-bytes";
|
|
17
19
|
import { toc } from "mdast-util-toc";
|
|
18
|
-
import { remark } from "remark";
|
|
19
|
-
import remarkGfm from "remark-gfm";
|
|
20
20
|
import { read, write } from "to-vfile";
|
|
21
21
|
import { VFile } from "vfile";
|
|
22
22
|
import { Configuration } from "unified-engine";
|
|
@@ -124,7 +124,8 @@ const GIT_PREFIX_REGEX = /^git\+/;
|
|
|
124
124
|
const GIT_SUFFIX_REGEX = /\.git$/;
|
|
125
125
|
const TRAILING_SLASH_REGEX = /\/$/;
|
|
126
126
|
/**
|
|
127
|
-
* Reset
|
|
127
|
+
* Reset cached context metadata. Call between tests or when the underlying
|
|
128
|
+
* project files may have changed on disk.
|
|
128
129
|
*
|
|
129
130
|
* @public
|
|
130
131
|
*/
|
|
@@ -163,7 +164,8 @@ async function getReadmeMetadata() {
|
|
|
163
164
|
return readmeMetadata;
|
|
164
165
|
}
|
|
165
166
|
/**
|
|
166
|
-
* Reset
|
|
167
|
+
* Reset cached readme metadata. Call between tests or when the underlying
|
|
168
|
+
* project files may have changed on disk.
|
|
167
169
|
*
|
|
168
170
|
* @public
|
|
169
171
|
*/
|
|
@@ -609,7 +611,7 @@ async function formatWithPrettier(content, filePath) {
|
|
|
609
611
|
if (config === void 0 && !configCache.has(configKey)) {
|
|
610
612
|
config = await cachedPrettier.resolveConfig(filePath ?? process.cwd());
|
|
611
613
|
configCache.set(configKey, config);
|
|
612
|
-
if (config) log.debug(`
|
|
614
|
+
if (config) log.debug(`Resolved Prettier config for "${configKey}"`);
|
|
613
615
|
}
|
|
614
616
|
return cachedPrettier.format(content, {
|
|
615
617
|
...config,
|
|
@@ -667,7 +669,8 @@ async function findReadme() {
|
|
|
667
669
|
}
|
|
668
670
|
/**
|
|
669
671
|
* Searches up for a readme.md file.
|
|
670
|
-
*
|
|
672
|
+
*
|
|
673
|
+
* @throws {Error} If no readme is found
|
|
671
674
|
*/
|
|
672
675
|
async function findReadmeThrows() {
|
|
673
676
|
const readme = await findReadme();
|
|
@@ -748,17 +751,26 @@ function getExpandProcessor(config, ambientRemarkConfig) {
|
|
|
748
751
|
emphasis: "_"
|
|
749
752
|
} }).use(remarkGfm).use(ambientRemarkConfig).use(() => async function(tree, file) {
|
|
750
753
|
mdatSplit(tree, file);
|
|
751
|
-
|
|
754
|
+
mdatCollapse(tree, file);
|
|
752
755
|
await mdatExpand(tree, file, config);
|
|
753
756
|
});
|
|
754
757
|
}
|
|
755
|
-
function
|
|
758
|
+
function getCollapseProcessor(_config, ambientRemarkConfig) {
|
|
756
759
|
return remark().use({ settings: {
|
|
757
760
|
bullet: "-",
|
|
758
761
|
emphasis: "_"
|
|
759
762
|
} }).use(remarkGfm).use(ambientRemarkConfig).use(() => function(tree, file) {
|
|
760
763
|
mdatSplit(tree, file);
|
|
761
|
-
|
|
764
|
+
mdatCollapse(tree, file);
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
function getStripProcessor(_config, ambientRemarkConfig) {
|
|
768
|
+
return remark().use({ settings: {
|
|
769
|
+
bullet: "-",
|
|
770
|
+
emphasis: "_"
|
|
771
|
+
} }).use(remarkGfm).use(ambientRemarkConfig).use(() => function(tree, file) {
|
|
772
|
+
mdatSplit(tree, file);
|
|
773
|
+
mdatStrip(tree, file);
|
|
762
774
|
});
|
|
763
775
|
}
|
|
764
776
|
//#endregion
|
|
@@ -874,9 +886,8 @@ async function createReadme(options) {
|
|
|
874
886
|
return readmePath;
|
|
875
887
|
}
|
|
876
888
|
function getTemplateForConfig(templateKey, compound) {
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
return templateString;
|
|
889
|
+
if (!(templateKey in templates_default)) throw new Error(`Unknown template "${templateKey}". Available templates: ${Object.keys(templates_default).join(", ")}`);
|
|
890
|
+
return templates_default[templateKey].content[compound ? "compound" : "explicit"];
|
|
880
891
|
}
|
|
881
892
|
function getTemplateOptions() {
|
|
882
893
|
return Object.entries(templates_default).map(([key, value]) => ({
|
|
@@ -888,10 +899,11 @@ function getTemplateOptions() {
|
|
|
888
899
|
//#endregion
|
|
889
900
|
//#region src/lib/api.ts
|
|
890
901
|
/**
|
|
891
|
-
* Expand MDAT comments in one or more Markdown files.
|
|
892
|
-
*
|
|
893
|
-
*
|
|
894
|
-
*
|
|
902
|
+
* Expand MDAT comments in one or more Markdown files. If no files are provided,
|
|
903
|
+
* auto-finds the closest readme.md. Writing is the responsibility of the caller
|
|
904
|
+
* (e.g. via `await write(result)`)
|
|
905
|
+
*
|
|
906
|
+
* @returns An array of VFiles
|
|
895
907
|
*/
|
|
896
908
|
async function expand(files, name, output, config, options) {
|
|
897
909
|
files ??= await findReadmeThrows();
|
|
@@ -908,14 +920,15 @@ async function expandString(markdown, config, options) {
|
|
|
908
920
|
return result;
|
|
909
921
|
}
|
|
910
922
|
/**
|
|
911
|
-
* Collapse MDAT comments in one or more Markdown files.
|
|
912
|
-
*
|
|
913
|
-
*
|
|
914
|
-
*
|
|
923
|
+
* Collapse MDAT comments in one or more Markdown files. If no files are
|
|
924
|
+
* provided, auto-finds the closest readme.md. Writing is the responsibility of
|
|
925
|
+
* the caller (e.g. via `await write(result)`)
|
|
926
|
+
*
|
|
927
|
+
* @returns An array of VFiles
|
|
915
928
|
*/
|
|
916
929
|
async function collapse(files, name, output, config, options) {
|
|
917
930
|
files ??= await findReadmeThrows();
|
|
918
|
-
const results = await processFiles(files, loadConfig,
|
|
931
|
+
const results = await processFiles(files, loadConfig, getCollapseProcessor, name, output, config);
|
|
919
932
|
if (options?.format) await formatResults(results);
|
|
920
933
|
return results;
|
|
921
934
|
}
|
|
@@ -923,14 +936,37 @@ async function collapse(files, name, output, config, options) {
|
|
|
923
936
|
* Collapse MDAT comments in a Markdown string.
|
|
924
937
|
*/
|
|
925
938
|
async function collapseString(markdown, config, options) {
|
|
926
|
-
const result = await processString(markdown, loadConfig,
|
|
939
|
+
const result = await processString(markdown, loadConfig, getCollapseProcessor, config);
|
|
927
940
|
if (options?.format) await formatResults([result]);
|
|
928
941
|
return result;
|
|
929
942
|
}
|
|
930
943
|
/**
|
|
931
|
-
*
|
|
932
|
-
*
|
|
933
|
-
*
|
|
944
|
+
* Strips MDAT comments in one or more Markdown files without touching other
|
|
945
|
+
* content. Does _not_ automatically expand Mdat content before stripping the
|
|
946
|
+
* tags. If no files are provided, auto-finds the closest readme.md. Writing is
|
|
947
|
+
* the responsibility of the caller (e.g. via `await write(result)`)
|
|
948
|
+
*
|
|
949
|
+
* @returns An array of VFiles
|
|
950
|
+
*/
|
|
951
|
+
async function strip(files, name, output, config, options) {
|
|
952
|
+
files ??= await findReadmeThrows();
|
|
953
|
+
const results = await processFiles(files, loadConfig, getStripProcessor, name, output, config);
|
|
954
|
+
if (options?.format) await formatResults(results);
|
|
955
|
+
return results;
|
|
956
|
+
}
|
|
957
|
+
/**
|
|
958
|
+
* Strip MDAT comments from a Markdown string.
|
|
959
|
+
*/
|
|
960
|
+
async function stripString(markdown, config, options) {
|
|
961
|
+
const result = await processString(markdown, loadConfig, getStripProcessor, config);
|
|
962
|
+
if (options?.format) await formatResults([result]);
|
|
963
|
+
return result;
|
|
964
|
+
}
|
|
965
|
+
/**
|
|
966
|
+
* Dry-run expand and compare with file on disk. If no files are provided,
|
|
967
|
+
* auto-finds the closest readme.md.
|
|
968
|
+
*
|
|
969
|
+
* @returns Per-file sync status and expanded VFiles
|
|
934
970
|
*/
|
|
935
971
|
async function check(files, config, options) {
|
|
936
972
|
files ??= await findReadmeThrows();
|
|
@@ -938,13 +974,40 @@ async function check(files, config, options) {
|
|
|
938
974
|
const resolvedFiles = Array.isArray(files) ? files : [files];
|
|
939
975
|
const [originals, results] = await Promise.all([Promise.all(resolvedFiles.map(async (f) => read(f))), processFiles(files, loadConfig, getExpandProcessor, void 0, void 0, config)]);
|
|
940
976
|
if (options?.format) await formatResults(results);
|
|
941
|
-
return results.map((result, i) => (
|
|
942
|
-
|
|
977
|
+
return results.map((result, i) => compareWithDiff(originals[i], result, options));
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Check if MDAT comments in a Markdown string are up to date by expanding and
|
|
981
|
+
* comparing per-tag.
|
|
982
|
+
*/
|
|
983
|
+
async function checkString(markdown, config, options) {
|
|
984
|
+
const { VFile: VF } = await import("vfile");
|
|
985
|
+
const original = new VF(markdown);
|
|
986
|
+
const result = await processString(markdown, loadConfig, getExpandProcessor, config);
|
|
987
|
+
if (options?.format) await formatResults([result]);
|
|
988
|
+
return compareWithDiff(original, result, options);
|
|
989
|
+
}
|
|
990
|
+
function compareWithDiff(original, result, options) {
|
|
991
|
+
const inSync = original.toString().replaceAll("\r\n", "\n") === result.toString();
|
|
992
|
+
if (!inSync) {
|
|
993
|
+
const parser = remark().use(remarkGfm);
|
|
994
|
+
const originalTree = parser.parse(original.toString());
|
|
995
|
+
mdatSplit(originalTree, original);
|
|
996
|
+
const expandedTree = parser.parse(result.toString());
|
|
997
|
+
mdatSplit(expandedTree, result);
|
|
998
|
+
const diffResults = mdatDiff(originalTree, original, expandedTree, result);
|
|
999
|
+
if (options?.format && diffResults.every((r) => r.status === "ok")) {
|
|
1000
|
+
const message = result.message("Formatting differences outside mdat tags", { source: "diff" });
|
|
1001
|
+
message.fatal = false;
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
return {
|
|
1005
|
+
inSync,
|
|
943
1006
|
result
|
|
944
|
-
}
|
|
1007
|
+
};
|
|
945
1008
|
}
|
|
946
1009
|
async function formatResults(results) {
|
|
947
1010
|
for (const file of results) file.value = await formatWithPrettier(file.toString(), file.path || void 0);
|
|
948
1011
|
}
|
|
949
1012
|
//#endregion
|
|
950
|
-
export { check, collapse, collapseString, createReadme as create, createReadmeInteractive as createInteractive, defineConfig, expand, expandString, getContextMetadata, getReadmeMetadata, loadConfig, mergeConfig, resetMetadataCaches, setLogger };
|
|
1013
|
+
export { check, checkString, collapse, collapseString, createReadme as create, createReadmeInteractive as createInteractive, defineConfig, expand, expandString, getContextMetadata, getReadmeMetadata, loadConfig, mergeConfig, resetMetadataCaches, setLogger, strip, stripString };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mdat",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.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",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"globby": "^16.2.0",
|
|
51
51
|
"lognow": "^0.6.0",
|
|
52
52
|
"mdast-util-toc": "^7.1.0",
|
|
53
|
-
"metascope": "^0.
|
|
53
|
+
"metascope": "^0.6.0",
|
|
54
54
|
"path-type": "^6.0.0",
|
|
55
55
|
"picocolors": "^1.1.1",
|
|
56
56
|
"plur": "^6.0.0",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"pretty-ms": "^9.3.0",
|
|
59
59
|
"remark": "^15.0.1",
|
|
60
60
|
"remark-gfm": "^4.0.1",
|
|
61
|
-
"remark-mdat": "^2.0
|
|
61
|
+
"remark-mdat": "^2.2.0",
|
|
62
62
|
"to-vfile": "^8.0.0",
|
|
63
63
|
"type-fest": "^5.5.0",
|
|
64
64
|
"unified-engine": "^11.2.2",
|
|
@@ -69,16 +69,17 @@
|
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@arethetypeswrong/core": "^0.18.2",
|
|
72
|
-
"@kitschpatrol/shared-config": "^7.0.
|
|
72
|
+
"@kitschpatrol/shared-config": "^7.0.1",
|
|
73
73
|
"@types/mdast": "^4.0.4",
|
|
74
74
|
"@types/node": "~22.17.2",
|
|
75
75
|
"@types/unist": "^3.0.3",
|
|
76
76
|
"@types/yargs": "^17.0.35",
|
|
77
|
+
"@vitest/coverage-v8": "4.1.2",
|
|
77
78
|
"bumpp": "^11.0.1",
|
|
78
79
|
"execa": "^9.6.1",
|
|
79
80
|
"mdat-plugin-cli-help": "^2.0.2",
|
|
80
81
|
"mdat-plugin-example": "^2.0.0",
|
|
81
|
-
"mdat-plugin-tldraw": "^2.0.
|
|
82
|
+
"mdat-plugin-tldraw": "^2.0.1",
|
|
82
83
|
"nanoid": "^5.1.7",
|
|
83
84
|
"prettier": "^3.8.1",
|
|
84
85
|
"publint": "^0.3.18",
|
|
@@ -109,6 +110,7 @@
|
|
|
109
110
|
"bench:baseline": "vitest bench --run --outputJson test/benchmarks/baseline.json",
|
|
110
111
|
"build": "tsdown",
|
|
111
112
|
"clean": "git rm -f pnpm-lock.yaml ; git clean -fdX",
|
|
113
|
+
"coverage": "vitest --coverage --run",
|
|
112
114
|
"fix": "ksc fix",
|
|
113
115
|
"lint": "ksc lint",
|
|
114
116
|
"release": "bumpp --commit 'Release: %s' && pnpm run build && NPM_AUTH_TOKEN=$(op read 'op://Personal/npm/token') && pnpm publish",
|
package/readme.md
CHANGED
|
@@ -228,6 +228,7 @@ mdat [command] [files..] [options]
|
|
|
228
228
|
| ---------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
229
229
|
| `expand` | `[files..]` `[options]` | Expand MDAT placeholder comments. If no files are provided, the closest readme.md is expanded. _(Default command.)_ |
|
|
230
230
|
| `collapse` | `[files..]` `[options]` | Collapse MDAT placeholder comments. If no files are provided, the closest readme.md is collapsed. |
|
|
231
|
+
| `strip` | `[files..]` `[options]` | Strip MDAT comments while preserving expanded content. If no files are provided, the closest readme.md is stripped. |
|
|
231
232
|
| `check` | `[files..]` `[options]` | Check if MDAT placeholder comments are up to date. Exits with code 1 if any files have stale or unexpanded content. |
|
|
232
233
|
| `create` | `[options]` | Create a new Markdown file from a template. |
|
|
233
234
|
|
|
@@ -282,6 +283,30 @@ mdat collapse [files..] [options]
|
|
|
282
283
|
| `--help`<br>`-h` | Show help | `boolean` | |
|
|
283
284
|
| `--version`<br>`-v` | Show version number | `boolean` | |
|
|
284
285
|
|
|
286
|
+
#### Subcommand: `mdat strip`
|
|
287
|
+
|
|
288
|
+
Strip MDAT comments while preserving expanded content. If no files are provided, the closest readme.md is stripped.
|
|
289
|
+
|
|
290
|
+
Usage:
|
|
291
|
+
|
|
292
|
+
```txt
|
|
293
|
+
mdat strip [files..] [options]
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
| Positional Argument | Description | Type |
|
|
297
|
+
| ------------------- | ----------------------------------------------------------------------------------------------------- | -------- |
|
|
298
|
+
| `files` | Markdown file(s) with MDAT placeholder comments. If not provided, the closest readme.md file is used. | `string` |
|
|
299
|
+
|
|
300
|
+
| Option | Description | Type | Default |
|
|
301
|
+
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------- | --------- | --------------------------------------------------- |
|
|
302
|
+
| `--verbose` | Enable verbose logging. All verbose logs are prefixed with their log level and are printed to stderr for ease of redirection. | `boolean` | |
|
|
303
|
+
| `--output`<br>`-o` | Output file directory. | `string` | Same directory as input file. |
|
|
304
|
+
| `--name`<br>`-n` | Output file name. | `string` | Same name as input file. Overwrites the input file. |
|
|
305
|
+
| `--print` | Print the expanded Markdown to stdout instead of saving to a file. Ignores `--output` and `--name` options. | `boolean` | |
|
|
306
|
+
| `--format`<br>`-f` | Format the output with Prettier. Discovers Prettier config from the file path. Requires `prettier` as a peer dependency. | `boolean` | |
|
|
307
|
+
| `--help`<br>`-h` | Show help | `boolean` | |
|
|
308
|
+
| `--version`<br>`-v` | Show version number | `boolean` | |
|
|
309
|
+
|
|
285
310
|
#### Subcommand: `mdat check`
|
|
286
311
|
|
|
287
312
|
Check if MDAT placeholder comments are up to date. Exits with code 1 if any files have stale or unexpanded content.
|
|
@@ -366,6 +391,12 @@ mdat check
|
|
|
366
391
|
mdat collapse
|
|
367
392
|
```
|
|
368
393
|
|
|
394
|
+
##### Strip MDAT comments from expanded content
|
|
395
|
+
|
|
396
|
+
```sh
|
|
397
|
+
mdat strip
|
|
398
|
+
```
|
|
399
|
+
|
|
369
400
|
##### Expand and format with Prettier
|
|
370
401
|
|
|
371
402
|
```sh
|
|
@@ -382,19 +413,9 @@ mdat create
|
|
|
382
413
|
|
|
383
414
|
`mdat` exports functions for expanding, collapsing, checking, and creating Markdown files programmatically.
|
|
384
415
|
|
|
385
|
-
#### `expand`
|
|
416
|
+
#### `expand` / `expandString`
|
|
386
417
|
|
|
387
|
-
|
|
388
|
-
function expand(
|
|
389
|
-
files?: string | string[],
|
|
390
|
-
name?: string,
|
|
391
|
-
output?: string,
|
|
392
|
-
config?: ConfigToLoad,
|
|
393
|
-
options?: { format?: boolean },
|
|
394
|
-
): Promise<VFile[]>
|
|
395
|
-
```
|
|
396
|
-
|
|
397
|
-
Expands MDAT comments in one or more files. If no files are provided, auto-finds the closest readme. Writing is the caller's responsibility:
|
|
418
|
+
Expands MDAT comments in one or more files. If no files are provided, auto-finds the closest readme. Writing is the caller's responsibility. Call `.toString()` on the returned [VFile](https://github.com/vfile) to get the result.
|
|
398
419
|
|
|
399
420
|
```ts
|
|
400
421
|
import { expand } from 'mdat'
|
|
@@ -404,8 +425,6 @@ const [file] = await expand('readme.md')
|
|
|
404
425
|
await write(file)
|
|
405
426
|
```
|
|
406
427
|
|
|
407
|
-
#### `expandString`
|
|
408
|
-
|
|
409
428
|
```ts
|
|
410
429
|
function expandString(
|
|
411
430
|
markdown: string,
|
|
@@ -414,23 +433,19 @@ function expandString(
|
|
|
414
433
|
): Promise<VFile>
|
|
415
434
|
```
|
|
416
435
|
|
|
417
|
-
Expands MDAT comments in a Markdown string. Call `.toString()` on the returned [VFile](https://github.com/vfile) to get the result.
|
|
418
|
-
|
|
419
436
|
#### `collapse` / `collapseString`
|
|
420
437
|
|
|
421
438
|
Removes expanded content, leaving only the opening comment placeholders. Same signatures as `expand` / `expandString`.
|
|
422
439
|
|
|
423
|
-
#### `
|
|
440
|
+
#### `strip` / `stripString`
|
|
424
441
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
config?: ConfigToLoad,
|
|
429
|
-
options?: { format?: boolean },
|
|
430
|
-
): Promise<{ inSync: boolean; results: VFile[] }>
|
|
431
|
-
```
|
|
442
|
+
Strips all MDAT comment tags (both opening and closing) while preserving expanded content between them. Same signatures as `expand` / `expandString` (without the `config` parameter, since rules are not needed).
|
|
443
|
+
|
|
444
|
+
This is useful for producing a "clean" Markdown file that no longer depends on MDAT for future updates.
|
|
432
445
|
|
|
433
|
-
|
|
446
|
+
#### `check` / `checkString`
|
|
447
|
+
|
|
448
|
+
Dry-run expand and compare with the file on disk. Returns `inSync: false` if the file would change. When stale, per-tag diagnostic messages are added to the result VFile identifying which specific tags are stale or unexpanded. Use `reporterMdat` from `remark-mdat` to display these messages.
|
|
434
449
|
|
|
435
450
|
#### `create` / `createInteractive`
|
|
436
451
|
|
|
@@ -577,16 +592,16 @@ See the [Examples section](https://github.com/kitschpatrol/remark-mdat#examples)
|
|
|
577
592
|
|
|
578
593
|
Embeds a file's size, with optional Brotli or Gzip compressed size.
|
|
579
594
|
|
|
580
|
-
- ##### `<!-- size-table({ files: [".gitignore", "
|
|
595
|
+
- ##### `<!-- size-table({ files: [".gitignore", "license.txt"] }) -->`
|
|
581
596
|
|
|
582
597
|
A table of files and their compressed sizes:
|
|
583
598
|
|
|
584
|
-
<!-- size-table({ files: [".gitignore", "
|
|
599
|
+
<!-- size-table({ files: [".gitignore", "license.txt"] }) -->
|
|
585
600
|
|
|
586
|
-
| File
|
|
587
|
-
|
|
|
588
|
-
| .gitignore
|
|
589
|
-
|
|
|
601
|
+
| File | Original | Gzip | Brotli |
|
|
602
|
+
| ----------- | -------- | ----- | ------ |
|
|
603
|
+
| .gitignore | 305 B | 245 B | 216 B |
|
|
604
|
+
| license.txt | 1 kB | 659 B | 468 B |
|
|
590
605
|
|
|
591
606
|
<!-- /size-table -->
|
|
592
607
|
|
|
@@ -779,30 +794,6 @@ There's quite a bit of prior art and similar explorations of this problem space:
|
|
|
779
794
|
|
|
780
795
|
- VitePress' [Markdown file inclusion](https://vitepress.dev/guide/markdown#markdown-file-inclusion)
|
|
781
796
|
|
|
782
|
-
There's quite a bit of prior art and similar explorations of this problem space:
|
|
783
|
-
|
|
784
|
-
- Benjamin Lupton's [projectz](https://github.com/bevry/projectz)\
|
|
785
|
-
Goes way back.
|
|
786
|
-
|
|
787
|
-
- David Wells' [Markdown Magic](https://github.com/DavidWells/markdown-magic)\
|
|
788
|
-
I somehow missed the existence of this one until after building out MDAT. It's very similar conceptually, and has a nice ecosystem of plugins.
|
|
789
|
-
|
|
790
|
-
- Titus Wormer's [mdast-zone](https://github.com/syntax-tree/mdast-zone)\
|
|
791
|
-
Allows comments to be used as ranges or markers in Markdown files. Similar tree parsing and walking strategy to MDAT. Mdast-zone uses different syntax for arguments, and requires both opening and closing tags to be present for expansion to occur.
|
|
792
|
-
|
|
793
|
-
- Jason Dent's [inject-markdown](https://github.com/streetsidesoftware/inject-markdown)
|
|
794
|
-
|
|
795
|
-
- lillallol's [md-in-place](https://www.npmjs.com/package/md-in-place)
|
|
796
|
-
|
|
797
|
-
- [AutoMD](https://automd.unjs.io/)\
|
|
798
|
-
Extremely similar functionality to mdat. The project was initiated around the same time as MDAT, but I didn't find the project until a few years later. Ships in the night.
|
|
799
|
-
|
|
800
|
-
- Franck Abgrall's [readme-md-generator](https://github.com/kefranabg/readme-md-generator)
|
|
801
|
-
|
|
802
|
-
- Anders Pitman's [tuplates](https://github.com/anderspitman/tuplates-py)
|
|
803
|
-
|
|
804
|
-
- VitePress' [Markdown file inclusion](https://vitepress.dev/guide/markdown#markdown-file-inclusion)
|
|
805
|
-
|
|
806
797
|
### Implementation notes
|
|
807
798
|
|
|
808
799
|
This project was split from a monorepo containing both `mdat` and `remark-mdat` into separate repos in July 2024.
|