mdat 2.0.0 → 2.1.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 +211 -119
- package/dist/lib/index.d.ts +44 -17
- package/dist/lib/index.js +222 -113
- package/package.json +11 -6
- package/readme.md +90 -16
- package/dist/.DS_Store +0 -0
package/dist/bin/cli.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
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, mdatExpand, mdatSplit, mdatStrip, reporterMdat, rulesSchema, setLogger } from "remark-mdat";
|
|
5
5
|
import { cosmiconfig, defaultLoaders } from "cosmiconfig";
|
|
6
6
|
import { TypeScriptLoader } from "cosmiconfig-typescript-loader";
|
|
7
7
|
import fs from "node:fs/promises";
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import plur from "plur";
|
|
10
|
-
import { defineTemplate, getMetadata, helpers, setLogger as setLogger$1, templates } from "metascope";
|
|
11
10
|
import { deepmerge } from "deepmerge-ts";
|
|
12
11
|
import { createLogger, getChildLogger, injectionHelper } from "lognow";
|
|
12
|
+
import { defineTemplate, getMetadata, helpers, setLogger as setLogger$1, templates } from "metascope";
|
|
13
13
|
import { z } from "zod";
|
|
14
14
|
import { globby } from "globby";
|
|
15
15
|
import { isFile, isFileSync } from "path-type";
|
|
@@ -27,65 +27,6 @@ import { confirm, group, intro, note, outro, select } from "@clack/prompts";
|
|
|
27
27
|
import yargs from "yargs";
|
|
28
28
|
import { hideBin } from "yargs/helpers";
|
|
29
29
|
|
|
30
|
-
//#region src/lib/context.ts
|
|
31
|
-
let metascopeMetadata;
|
|
32
|
-
/**
|
|
33
|
-
* Get a bunch of platform-agnostic local metadata via metascope, exposed
|
|
34
|
-
* primarily for plugin developers.
|
|
35
|
-
* Result is memoized the result.
|
|
36
|
-
* @throws {Error} If no package.json is found
|
|
37
|
-
*/
|
|
38
|
-
async function getContextMetadata() {
|
|
39
|
-
if (metascopeMetadata !== void 0) return metascopeMetadata;
|
|
40
|
-
metascopeMetadata = await getMetadata({
|
|
41
|
-
absolute: false,
|
|
42
|
-
offline: true
|
|
43
|
-
});
|
|
44
|
-
return metascopeMetadata;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Reset
|
|
48
|
-
*/
|
|
49
|
-
function resetContextMetadata() {
|
|
50
|
-
metascopeMetadata = void 0;
|
|
51
|
-
}
|
|
52
|
-
const readmeMetadataTemplate = defineTemplate((context) => {
|
|
53
|
-
const { githubActions, licenseFile, metascope, nodePackageJson } = context;
|
|
54
|
-
const codemeta = templates.codemetaJson(context, {});
|
|
55
|
-
const nodePackage = helpers.firstOf(nodePackageJson)?.data;
|
|
56
|
-
const licenseFileData = helpers.firstOf(licenseFile);
|
|
57
|
-
const ciActionFilePath = helpers.ensureArray(githubActions).find((entry) => entry.data.name.toLowerCase() === "ci")?.source;
|
|
58
|
-
const repositoryOwner = codemeta.codeRepository ? new URL(codemeta.codeRepository).pathname.split("/")[1] : void 0;
|
|
59
|
-
return {
|
|
60
|
-
author: helpers.firstOf(helpers.mixedStringsToArray(helpers.toBasicNames(codemeta.author))),
|
|
61
|
-
ciActionFileName: ciActionFilePath ? path.basename(ciActionFilePath) : void 0,
|
|
62
|
-
description: codemeta.description,
|
|
63
|
-
isPublicNpmPackage: !nodePackage?.name.startsWith("@") || nodePackage.publishConfig?.access === "public",
|
|
64
|
-
issuesUrl: codemeta.issueTracker,
|
|
65
|
-
license: helpers.toBasicLicense(helpers.firstOf(helpers.ensureArray(codemeta.license))),
|
|
66
|
-
licenseFilePath: licenseFileData?.source,
|
|
67
|
-
name: codemeta.name,
|
|
68
|
-
projectDirectory: metascope?.data.options.path === void 0 ? void 0 : `file://${metascope.data.options.path}`,
|
|
69
|
-
repositoryOwner
|
|
70
|
-
};
|
|
71
|
-
});
|
|
72
|
-
let readmeMetadata;
|
|
73
|
-
/**
|
|
74
|
-
* Nice data for readme rules
|
|
75
|
-
*/
|
|
76
|
-
async function getReadmeMetadata() {
|
|
77
|
-
if (readmeMetadata !== void 0) return readmeMetadata;
|
|
78
|
-
readmeMetadata = readmeMetadataTemplate(await getContextMetadata(), {});
|
|
79
|
-
return readmeMetadata;
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Reset
|
|
83
|
-
*/
|
|
84
|
-
function resetReadmeMetadata() {
|
|
85
|
-
readmeMetadata = void 0;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
//#endregion
|
|
89
30
|
//#region src/lib/deep-merge-defined.ts
|
|
90
31
|
function stripUndefinedDeep(object) {
|
|
91
32
|
if (Array.isArray(object)) return object.map((v) => v && typeof v === "object" ? stripUndefinedDeep(v) : v).filter((v) => v !== void 0);
|
|
@@ -101,7 +42,7 @@ function deepMergeDefined(...objects) {
|
|
|
101
42
|
//#endregion
|
|
102
43
|
//#region package.json
|
|
103
44
|
var name = "mdat";
|
|
104
|
-
var version = "2.
|
|
45
|
+
var version = "2.1.0";
|
|
105
46
|
|
|
106
47
|
//#endregion
|
|
107
48
|
//#region src/lib/log.ts
|
|
@@ -146,6 +87,87 @@ function flattenJson(jsonObject, parentKey = "", result = {}) {
|
|
|
146
87
|
return result;
|
|
147
88
|
}
|
|
148
89
|
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/lib/context.ts
|
|
92
|
+
let metascopeMetadata;
|
|
93
|
+
/**
|
|
94
|
+
* Get a bunch of platform-agnostic local metadata via metascope, exposed
|
|
95
|
+
* primarily for plugin developers. Result is memoized the result.
|
|
96
|
+
*
|
|
97
|
+
* @throws {Error} If no package.json is found
|
|
98
|
+
*/
|
|
99
|
+
async function getContextMetadata() {
|
|
100
|
+
if (metascopeMetadata !== void 0) return metascopeMetadata;
|
|
101
|
+
metascopeMetadata = await getMetadata({
|
|
102
|
+
absolute: false,
|
|
103
|
+
offline: true,
|
|
104
|
+
sources: [
|
|
105
|
+
"arduinoLibraryProperties",
|
|
106
|
+
"cinderCinderblockXml",
|
|
107
|
+
"codemetaJson",
|
|
108
|
+
"gitConfig",
|
|
109
|
+
"githubActions",
|
|
110
|
+
"goGoMod",
|
|
111
|
+
"goGoreleaserYaml",
|
|
112
|
+
"javaPomXml",
|
|
113
|
+
"licenseFile",
|
|
114
|
+
"metadataFile",
|
|
115
|
+
"metascope",
|
|
116
|
+
"nodePackageJson",
|
|
117
|
+
"obsidianPluginManifestJson",
|
|
118
|
+
"openframeworksAddonConfigMk",
|
|
119
|
+
"openframeworksInstallXml",
|
|
120
|
+
"processingLibraryProperties",
|
|
121
|
+
"processingSketchProperties",
|
|
122
|
+
"publiccodeYaml",
|
|
123
|
+
"pythonPkgInfo",
|
|
124
|
+
"pythonPyprojectToml",
|
|
125
|
+
"pythonSetupCfg",
|
|
126
|
+
"pythonSetupPy",
|
|
127
|
+
"readmeFile",
|
|
128
|
+
"rubyGemspec",
|
|
129
|
+
"rustCargoToml",
|
|
130
|
+
"xcodeInfoPlist",
|
|
131
|
+
"xcodeProjectPbxproj"
|
|
132
|
+
]
|
|
133
|
+
});
|
|
134
|
+
return metascopeMetadata;
|
|
135
|
+
}
|
|
136
|
+
const GIT_PREFIX_REGEX = /^git\+/;
|
|
137
|
+
const GIT_SUFFIX_REGEX = /\.git$/;
|
|
138
|
+
const TRAILING_SLASH_REGEX = /\/$/;
|
|
139
|
+
const readmeMetadataTemplate = defineTemplate((context) => {
|
|
140
|
+
const { githubActions, licenseFile, metascope, nodePackageJson } = context;
|
|
141
|
+
const codemeta = templates.codemetaJson(context, {});
|
|
142
|
+
const nodePackage = helpers.firstOf(nodePackageJson)?.data;
|
|
143
|
+
const licenseFileData = helpers.firstOf(licenseFile);
|
|
144
|
+
const ciActionFilePath = helpers.ensureArray(githubActions).find((entry) => entry.data.name.toLowerCase() === "ci")?.source;
|
|
145
|
+
const repositoryUrl = codemeta.codeRepository?.replace(GIT_PREFIX_REGEX, "").replace(GIT_SUFFIX_REGEX, "").replace(TRAILING_SLASH_REGEX, "");
|
|
146
|
+
return {
|
|
147
|
+
author: helpers.firstOf(helpers.mixedStringsToArray(helpers.toBasicNames(codemeta.author))),
|
|
148
|
+
ciActionFileName: ciActionFilePath ? path.basename(ciActionFilePath) : void 0,
|
|
149
|
+
description: codemeta.description,
|
|
150
|
+
isPublicNpmPackage: !nodePackage?.name.startsWith("@") || nodePackage.publishConfig?.access === "public",
|
|
151
|
+
issuesUrl: codemeta.issueTracker,
|
|
152
|
+
license: helpers.toBasicLicense(helpers.firstOf(helpers.ensureArray(codemeta.license))),
|
|
153
|
+
licenseFilePath: licenseFileData?.source,
|
|
154
|
+
name: codemeta.name,
|
|
155
|
+
projectDirectory: metascope?.data.options.path === void 0 ? void 0 : `file://${metascope.data.options.path}`,
|
|
156
|
+
repositoryUrl
|
|
157
|
+
};
|
|
158
|
+
});
|
|
159
|
+
let readmeMetadata;
|
|
160
|
+
/**
|
|
161
|
+
* Nice data for readme rules
|
|
162
|
+
*
|
|
163
|
+
* @public
|
|
164
|
+
*/
|
|
165
|
+
async function getReadmeMetadata() {
|
|
166
|
+
if (readmeMetadata !== void 0) return readmeMetadata;
|
|
167
|
+
readmeMetadata = readmeMetadataTemplate(await getContextMetadata(), {});
|
|
168
|
+
return readmeMetadata;
|
|
169
|
+
}
|
|
170
|
+
|
|
149
171
|
//#endregion
|
|
150
172
|
//#region src/lib/readme/rules/badges.ts
|
|
151
173
|
var badges_default = { badges: { async content(options) {
|
|
@@ -157,13 +179,13 @@ var badges_default = { badges: { async content(options) {
|
|
|
157
179
|
npm: z.array(z.string()).optional()
|
|
158
180
|
}).optional().parse(options);
|
|
159
181
|
const metadata = await getReadmeMetadata();
|
|
160
|
-
const { ciActionFileName, license, name,
|
|
182
|
+
const { ciActionFileName, license, name, repositoryUrl } = metadata;
|
|
161
183
|
const badges = [];
|
|
162
184
|
if (validOptions?.npm === void 0) {
|
|
163
185
|
if (metadata.isPublicNpmPackage) badges.push(`[](https://npmjs.com/package/${name})`);
|
|
164
186
|
} else for (const name of validOptions.npm) badges.push(`[](https://npmjs.com/package/${name})`);
|
|
165
187
|
if (license !== void 0) badges.push(`[}-yellow.svg)](https://opensource.org/licenses/${license})`);
|
|
166
|
-
if (ciActionFileName !== void 0 &&
|
|
188
|
+
if (ciActionFileName !== void 0 && repositoryUrl !== void 0) badges.push(`[](${repositoryUrl}/actions/workflows/${ciActionFileName})`);
|
|
167
189
|
if (validOptions?.custom !== void 0) for (const [name, { image, link }] of Object.entries(validOptions.custom)) badges.push(`[](${link})`);
|
|
168
190
|
return badges.join("\n");
|
|
169
191
|
} } };
|
|
@@ -231,24 +253,21 @@ function isUrl(text, lenient = true) {
|
|
|
231
253
|
if (typeof text !== "string") throw new TypeError("Expected a string");
|
|
232
254
|
text = text.trim();
|
|
233
255
|
if (text.includes(" ")) return false;
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
} catch {
|
|
238
|
-
if (lenient) return isUrl(`https://${text}`, false);
|
|
239
|
-
return false;
|
|
240
|
-
}
|
|
256
|
+
if (URL.canParse(text)) return true;
|
|
257
|
+
if (lenient) return isUrl(`https://${text}`, false);
|
|
258
|
+
return false;
|
|
241
259
|
}
|
|
242
260
|
|
|
243
261
|
//#endregion
|
|
244
262
|
//#region src/lib/readme/rules/code.ts
|
|
263
|
+
const LEADING_DOT_REGEX = /^\./;
|
|
245
264
|
var code_default = { code: { async content(options) {
|
|
246
265
|
const validOptions = z.object({
|
|
247
266
|
file: z.string(),
|
|
248
267
|
language: z.string().optional(),
|
|
249
268
|
trim: z.boolean().default(true)
|
|
250
269
|
}).parse(options);
|
|
251
|
-
const lang = (path.extname(validOptions.file) ?? "").replace(
|
|
270
|
+
const lang = (path.extname(validOptions.file) ?? "").replace(LEADING_DOT_REGEX, "");
|
|
252
271
|
const exampleCode = await fs.readFile(path.join(process.cwd(), validOptions.file), "utf8");
|
|
253
272
|
return `\`\`\`${lang}\n${validOptions.trim ? exampleCode.trim() : exampleCode}\n\`\`\``;
|
|
254
273
|
} } };
|
|
@@ -309,8 +328,9 @@ var title_default = { title: {
|
|
|
309
328
|
},
|
|
310
329
|
order: 2
|
|
311
330
|
} };
|
|
331
|
+
const SPLIT_FOR_TITLE_CASE_REGEX = /[ _-]+/;
|
|
312
332
|
function makeTitleCase(text) {
|
|
313
|
-
return text.split(
|
|
333
|
+
return text.split(SPLIT_FOR_TITLE_CASE_REGEX).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
314
334
|
}
|
|
315
335
|
|
|
316
336
|
//#endregion
|
|
@@ -497,6 +517,19 @@ var rules_default = {
|
|
|
497
517
|
|
|
498
518
|
//#endregion
|
|
499
519
|
//#region src/lib/config.ts
|
|
520
|
+
let _configExplorer;
|
|
521
|
+
let _additionalConfigExplorer;
|
|
522
|
+
function getConfigExplorer() {
|
|
523
|
+
_configExplorer ??= cosmiconfig("mdat", { loaders: { ".ts": TypeScriptLoader() } });
|
|
524
|
+
return _configExplorer;
|
|
525
|
+
}
|
|
526
|
+
function getAdditionalConfigExplorer() {
|
|
527
|
+
_additionalConfigExplorer ??= cosmiconfig("mdat", { loaders: {
|
|
528
|
+
".json": mdatJsonLoader,
|
|
529
|
+
".ts": TypeScriptLoader()
|
|
530
|
+
} });
|
|
531
|
+
return _additionalConfigExplorer;
|
|
532
|
+
}
|
|
500
533
|
/**
|
|
501
534
|
* Load and validate mdat configuration.
|
|
502
535
|
* Uses cosmiconfig to search in the usual places.
|
|
@@ -504,11 +537,9 @@ var rules_default = {
|
|
|
504
537
|
*/
|
|
505
538
|
async function loadConfig(options) {
|
|
506
539
|
const { additionalConfig, defaults = rules_default, searchFrom } = options ?? {};
|
|
507
|
-
resetReadmeMetadata();
|
|
508
|
-
resetContextMetadata();
|
|
509
540
|
let finalConfig = { mdat: `Powered by the Markdown Autophagic Template system: [mdat](https://github.com/kitschpatrol/mdat).` };
|
|
510
541
|
if (defaults) finalConfig = deepMergeDefined(finalConfig, defaults);
|
|
511
|
-
const results = await
|
|
542
|
+
const results = await getConfigExplorer().search(searchFrom);
|
|
512
543
|
if (results) {
|
|
513
544
|
const { config, filepath } = results;
|
|
514
545
|
let possibleRules = config;
|
|
@@ -523,10 +554,6 @@ async function loadConfig(options) {
|
|
|
523
554
|
}
|
|
524
555
|
if (additionalConfig !== void 0) {
|
|
525
556
|
const additionalConfigArray = Array.isArray(additionalConfig) ? additionalConfig : [additionalConfig];
|
|
526
|
-
const configExplorer2 = cosmiconfig("mdat", { loaders: {
|
|
527
|
-
".json": mdatJsonLoader,
|
|
528
|
-
".ts": TypeScriptLoader()
|
|
529
|
-
} });
|
|
530
557
|
for (const configOrPath of additionalConfigArray) {
|
|
531
558
|
let loaded;
|
|
532
559
|
if (typeof configOrPath === "string") {
|
|
@@ -535,7 +562,7 @@ async function loadConfig(options) {
|
|
|
535
562
|
config: mdatJsonLoader(configOrPath, await fs.readFile(configOrPath, "utf8")),
|
|
536
563
|
filepath: configOrPath
|
|
537
564
|
};
|
|
538
|
-
else results = await
|
|
565
|
+
else results = await getAdditionalConfigExplorer().load(configOrPath);
|
|
539
566
|
if (results === null || results === void 0) continue;
|
|
540
567
|
const { config: loadedConfig, filepath } = results;
|
|
541
568
|
log.debug(`Loaded additional config from "${filepath}"`);
|
|
@@ -559,20 +586,26 @@ function validateConfig(value) {
|
|
|
559
586
|
|
|
560
587
|
//#endregion
|
|
561
588
|
//#region src/lib/format.ts
|
|
589
|
+
let cachedPrettier;
|
|
590
|
+
const configCache = /* @__PURE__ */ new Map();
|
|
562
591
|
/**
|
|
563
592
|
* Format a markdown string with Prettier, using config discovered from the file path.
|
|
564
593
|
* Requires `prettier` to be installed as a peer dependency.
|
|
565
594
|
*/
|
|
566
595
|
async function formatWithPrettier(content, filePath) {
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
prettier = await import("prettier");
|
|
596
|
+
if (cachedPrettier === void 0) try {
|
|
597
|
+
cachedPrettier = await import("prettier");
|
|
570
598
|
} catch {
|
|
571
599
|
throw new Error("The --format flag requires `prettier` to be installed. Run: pnpm add -D prettier");
|
|
572
600
|
}
|
|
573
|
-
const
|
|
574
|
-
|
|
575
|
-
|
|
601
|
+
const configKey = filePath ? path.dirname(filePath) : process.cwd();
|
|
602
|
+
let config = configCache.get(configKey);
|
|
603
|
+
if (config === void 0 && !configCache.has(configKey)) {
|
|
604
|
+
config = await cachedPrettier.resolveConfig(filePath ?? process.cwd());
|
|
605
|
+
configCache.set(configKey, config);
|
|
606
|
+
if (config) log.debug(`Using Prettier config from "${config.filepath}" for "${filePath ?? process.cwd()}"`);
|
|
607
|
+
}
|
|
608
|
+
return cachedPrettier.format(content, {
|
|
576
609
|
...config,
|
|
577
610
|
filepath: filePath,
|
|
578
611
|
parser: "markdown"
|
|
@@ -614,12 +647,13 @@ function ensureArray(value) {
|
|
|
614
647
|
if (value === void 0 || value === null) return [];
|
|
615
648
|
return Array.isArray(value) ? value : [value];
|
|
616
649
|
}
|
|
650
|
+
const README_SEARCH_REGEX = /^readme(?:\.\w+)?$/i;
|
|
617
651
|
/**
|
|
618
652
|
* Finds a readme file in the current working directory (case-insensitive).
|
|
619
653
|
*/
|
|
620
654
|
async function findReadme() {
|
|
621
655
|
log.debug("Searching for readme in current directory...");
|
|
622
|
-
const readme = (await fs.readdir(process.cwd())).find((entry) =>
|
|
656
|
+
const readme = (await fs.readdir(process.cwd())).find((entry) => README_SEARCH_REGEX.test(entry));
|
|
623
657
|
if (readme !== void 0) {
|
|
624
658
|
const absolutePath = path.resolve(readme);
|
|
625
659
|
log.debug(`Found readme at "${absolutePath}"`);
|
|
@@ -635,7 +669,9 @@ async function findReadmeThrows() {
|
|
|
635
669
|
if (readme === void 0) throw new Error("No readme found");
|
|
636
670
|
return readme;
|
|
637
671
|
}
|
|
672
|
+
let cachedAmbientRemarkConfig;
|
|
638
673
|
async function loadAmbientRemarkConfig() {
|
|
674
|
+
if (cachedAmbientRemarkConfig !== void 0) return cachedAmbientRemarkConfig;
|
|
639
675
|
const ambientConfig = new Configuration({
|
|
640
676
|
cwd: process.cwd(),
|
|
641
677
|
detectConfig: true,
|
|
@@ -656,32 +692,47 @@ async function loadAmbientRemarkConfig() {
|
|
|
656
692
|
const { filePath } = configResult;
|
|
657
693
|
if (filePath === void 0) log.debug("No ambient Remark configuration file found");
|
|
658
694
|
else log.debug(`Found and loaded ambient Remark configuration from "${filePath}"`);
|
|
659
|
-
|
|
695
|
+
cachedAmbientRemarkConfig = stripLintPlugins(configResult);
|
|
696
|
+
return cachedAmbientRemarkConfig;
|
|
660
697
|
}
|
|
661
698
|
log.debug("No ambient Remark configuration found");
|
|
662
|
-
|
|
699
|
+
cachedAmbientRemarkConfig = {
|
|
663
700
|
filePath: void 0,
|
|
664
701
|
plugins: [],
|
|
665
702
|
settings: {}
|
|
666
703
|
};
|
|
704
|
+
return cachedAmbientRemarkConfig;
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Strip remark-lint plugins from an ambient config. Lint plugins only produce
|
|
708
|
+
* VFile warnings and never modify the AST or output — running them during
|
|
709
|
+
* expansion is pure overhead.
|
|
710
|
+
*/
|
|
711
|
+
function stripLintPlugins(config) {
|
|
712
|
+
return {
|
|
713
|
+
...config,
|
|
714
|
+
plugins: config.plugins.filter((entry) => {
|
|
715
|
+
const plugin = Array.isArray(entry) ? entry[0] : entry;
|
|
716
|
+
if (typeof plugin !== "function") return true;
|
|
717
|
+
const { name } = plugin;
|
|
718
|
+
return name !== "remarkLint" && !name.startsWith("remark-lint:") && name !== "remarkValidateLinks";
|
|
719
|
+
})
|
|
720
|
+
};
|
|
667
721
|
}
|
|
668
722
|
|
|
669
723
|
//#endregion
|
|
670
724
|
//#region src/lib/processors.ts
|
|
671
725
|
async function processFiles(files, loader, processorGetter, name, output, config) {
|
|
672
|
-
const resolvedConfig = await loader({ additionalConfig: config });
|
|
673
|
-
const localRemarkConfiguration = await loadAmbientRemarkConfig();
|
|
726
|
+
const [resolvedConfig, localRemarkConfiguration] = await Promise.all([loader({ additionalConfig: config }), loadAmbientRemarkConfig()]);
|
|
674
727
|
const inputOutputPaths = await getInputOutputPaths(ensureArray(files), output, name, "md");
|
|
675
|
-
const results = [];
|
|
676
728
|
const resolvedProcessor = processorGetter(resolvedConfig, localRemarkConfiguration);
|
|
677
|
-
|
|
729
|
+
return await Promise.all(inputOutputPaths.map(async ({ input, name, output }) => {
|
|
678
730
|
const inputFile = await read(input);
|
|
679
731
|
const result = await resolvedProcessor.process(inputFile);
|
|
680
732
|
result.dirname = output;
|
|
681
733
|
result.basename = name;
|
|
682
|
-
|
|
683
|
-
}
|
|
684
|
-
return results;
|
|
734
|
+
return result;
|
|
735
|
+
}));
|
|
685
736
|
}
|
|
686
737
|
function getExpandProcessor(config, ambientRemarkConfig) {
|
|
687
738
|
return remark().use({ settings: {
|
|
@@ -689,17 +740,26 @@ function getExpandProcessor(config, ambientRemarkConfig) {
|
|
|
689
740
|
emphasis: "_"
|
|
690
741
|
} }).use(remarkGfm).use(ambientRemarkConfig).use(() => async function(tree, file) {
|
|
691
742
|
mdatSplit(tree, file);
|
|
692
|
-
|
|
743
|
+
mdatCollapse(tree, file);
|
|
693
744
|
await mdatExpand(tree, file, config);
|
|
694
745
|
});
|
|
695
746
|
}
|
|
696
|
-
function
|
|
747
|
+
function getCollapseProcessor(_config, ambientRemarkConfig) {
|
|
748
|
+
return remark().use({ settings: {
|
|
749
|
+
bullet: "-",
|
|
750
|
+
emphasis: "_"
|
|
751
|
+
} }).use(remarkGfm).use(ambientRemarkConfig).use(() => function(tree, file) {
|
|
752
|
+
mdatSplit(tree, file);
|
|
753
|
+
mdatCollapse(tree, file);
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
function getStripProcessor(_config, ambientRemarkConfig) {
|
|
697
757
|
return remark().use({ settings: {
|
|
698
758
|
bullet: "-",
|
|
699
759
|
emphasis: "_"
|
|
700
760
|
} }).use(remarkGfm).use(ambientRemarkConfig).use(() => function(tree, file) {
|
|
701
761
|
mdatSplit(tree, file);
|
|
702
|
-
|
|
762
|
+
mdatStrip(tree, file);
|
|
703
763
|
});
|
|
704
764
|
}
|
|
705
765
|
|
|
@@ -838,10 +898,11 @@ function getTemplateOptions() {
|
|
|
838
898
|
//#endregion
|
|
839
899
|
//#region src/lib/api.ts
|
|
840
900
|
/**
|
|
841
|
-
* Expand MDAT comments in one or more Markdown files.
|
|
842
|
-
*
|
|
843
|
-
*
|
|
844
|
-
*
|
|
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
|
|
845
906
|
*/
|
|
846
907
|
async function expand(files, name, output, config, options) {
|
|
847
908
|
files ??= await findReadmeThrows();
|
|
@@ -850,31 +911,46 @@ async function expand(files, name, output, config, options) {
|
|
|
850
911
|
return results;
|
|
851
912
|
}
|
|
852
913
|
/**
|
|
853
|
-
* Collapse MDAT comments in one or more Markdown files.
|
|
854
|
-
*
|
|
855
|
-
*
|
|
856
|
-
*
|
|
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
|
|
857
919
|
*/
|
|
858
920
|
async function collapse(files, name, output, config, options) {
|
|
859
921
|
files ??= await findReadmeThrows();
|
|
860
|
-
const results = await processFiles(files, loadConfig,
|
|
922
|
+
const results = await processFiles(files, loadConfig, getCollapseProcessor, name, output, config);
|
|
861
923
|
if (options?.format) await formatResults(results);
|
|
862
924
|
return results;
|
|
863
925
|
}
|
|
864
926
|
/**
|
|
865
|
-
*
|
|
866
|
-
*
|
|
867
|
-
*
|
|
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
|
|
868
945
|
*/
|
|
869
946
|
async function check(files, config, options) {
|
|
870
947
|
files ??= await findReadmeThrows();
|
|
871
948
|
const { read } = await import("to-vfile");
|
|
872
949
|
const resolvedFiles = Array.isArray(files) ? files : [files];
|
|
873
|
-
const originals = await Promise.all(resolvedFiles.map(async (f) => read(f)));
|
|
874
|
-
const results = await processFiles(files, loadConfig, getExpandProcessor, void 0, void 0, config);
|
|
950
|
+
const [originals, results] = await Promise.all([Promise.all(resolvedFiles.map(async (f) => read(f))), processFiles(files, loadConfig, getExpandProcessor, void 0, void 0, config)]);
|
|
875
951
|
if (options?.format) await formatResults(results);
|
|
876
952
|
return results.map((result, i) => ({
|
|
877
|
-
inSync: originals[i].toString() === result.toString(),
|
|
953
|
+
inSync: originals[i].toString().replaceAll("\r\n", "\n") === result.toString(),
|
|
878
954
|
result
|
|
879
955
|
}));
|
|
880
956
|
}
|
|
@@ -961,7 +1037,11 @@ try {
|
|
|
961
1037
|
setLogger$2(createLogger({
|
|
962
1038
|
name,
|
|
963
1039
|
verbose: argv.verbose ?? false,
|
|
964
|
-
logToConsole: {
|
|
1040
|
+
logToConsole: {
|
|
1041
|
+
showLevel: false,
|
|
1042
|
+
showName: false,
|
|
1043
|
+
showTime: false
|
|
1044
|
+
}
|
|
965
1045
|
}));
|
|
966
1046
|
}).command(["$0 [files..] [options]", "expand [files..] [options]"], "Expand MDAT placeholder comments. If no files are provided, the closest readme.md is expanded.", (yargs) => yargs.positional(...filesPositional).option(configOption).option(outputOption).option(nameOption).option(printOption).option(formatOption), async ({ config, files, format, name, output, print }) => {
|
|
967
1047
|
logConflicts({
|
|
@@ -987,14 +1067,26 @@ try {
|
|
|
987
1067
|
reporterMdat(results);
|
|
988
1068
|
log.debug(`Collapsed comments in ${prettyMilliseconds(performance.now() - startTime)}.`);
|
|
989
1069
|
process.exitCode = getExitCode(results);
|
|
990
|
-
}).command("
|
|
1070
|
+
}).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 }) => {
|
|
1071
|
+
logConflicts({
|
|
1072
|
+
name,
|
|
1073
|
+
output,
|
|
1074
|
+
print
|
|
1075
|
+
});
|
|
1076
|
+
const results = await strip(files, name, output, void 0, { format });
|
|
1077
|
+
for (const file of results) if (print) process.stdout.write(file.toString());
|
|
1078
|
+
else await write(file);
|
|
1079
|
+
reporterMdat(results);
|
|
1080
|
+
log.debug(`Stripped comments in ${prettyMilliseconds(performance.now() - startTime)}.`);
|
|
1081
|
+
process.exitCode = getExitCode(results);
|
|
1082
|
+
}).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 }) => {
|
|
991
1083
|
const results = await check(files, collectConfig(config), { format });
|
|
992
1084
|
let allInSync = true;
|
|
993
1085
|
for (const { inSync, result } of results) {
|
|
994
1086
|
const filePath = result.path || "unknown";
|
|
995
|
-
if (inSync) log.
|
|
1087
|
+
if (inSync) log.debug(`${picocolors.green("Up to date")}: ${filePath}`);
|
|
996
1088
|
else {
|
|
997
|
-
log.
|
|
1089
|
+
log.warn(`${picocolors.red("Stale content")}: ${filePath}`);
|
|
998
1090
|
allInSync = false;
|
|
999
1091
|
}
|
|
1000
1092
|
}
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Rule, Rule as Rule$1 } from "remark-mdat";
|
|
2
|
+
import { ILogBasic, ILogLayer } from "lognow";
|
|
2
3
|
import * as _$metascope from "metascope";
|
|
3
4
|
import { MetadataContext } from "metascope";
|
|
4
|
-
import { ILogBasic, ILogLayer } from "lognow";
|
|
5
5
|
import { VFile } from "vfile";
|
|
6
6
|
|
|
7
7
|
//#region src/lib/config.d.ts
|
|
@@ -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;
|
|
@@ -111,8 +131,8 @@ declare function check(files?: string | string[], config?: ConfigToLoad, options
|
|
|
111
131
|
//#region src/lib/context.d.ts
|
|
112
132
|
/**
|
|
113
133
|
* Get a bunch of platform-agnostic local metadata via metascope, exposed
|
|
114
|
-
* primarily for plugin developers.
|
|
115
|
-
*
|
|
134
|
+
* primarily for plugin developers. Result is memoized the result.
|
|
135
|
+
*
|
|
116
136
|
* @throws {Error} If no package.json is found
|
|
117
137
|
*/
|
|
118
138
|
declare function getContextMetadata(): Promise<MetadataContext>;
|
|
@@ -126,11 +146,13 @@ declare const readmeMetadataTemplate: _$metascope.Template<{
|
|
|
126
146
|
licenseFilePath: string | undefined;
|
|
127
147
|
name: string | undefined;
|
|
128
148
|
projectDirectory: string | undefined;
|
|
129
|
-
|
|
149
|
+
repositoryUrl: string | undefined;
|
|
130
150
|
}>;
|
|
131
151
|
type ReadmeMetadata = ReturnType<typeof readmeMetadataTemplate>;
|
|
132
152
|
/**
|
|
133
153
|
* Nice data for readme rules
|
|
154
|
+
*
|
|
155
|
+
* @public
|
|
134
156
|
*/
|
|
135
157
|
declare function getReadmeMetadata(): Promise<{
|
|
136
158
|
author: string | undefined;
|
|
@@ -142,8 +164,13 @@ declare function getReadmeMetadata(): Promise<{
|
|
|
142
164
|
licenseFilePath: string | undefined;
|
|
143
165
|
name: string | undefined;
|
|
144
166
|
projectDirectory: string | undefined;
|
|
145
|
-
|
|
167
|
+
repositoryUrl: string | undefined;
|
|
146
168
|
}>;
|
|
169
|
+
/**
|
|
170
|
+
* Reset all cached metadata. Call between tests or when the underlying project
|
|
171
|
+
* files may have changed on disk.
|
|
172
|
+
*/
|
|
173
|
+
declare function resetMetadataCaches(): void;
|
|
147
174
|
//#endregion
|
|
148
175
|
//#region src/lib/log.d.ts
|
|
149
176
|
/**
|
|
@@ -153,4 +180,4 @@ declare function getReadmeMetadata(): Promise<{
|
|
|
153
180
|
*/
|
|
154
181
|
declare function setLogger(logger?: ILogBasic | ILogLayer): void;
|
|
155
182
|
//#endregion
|
|
156
|
-
export { type Config, type ReadmeMetadata, type Rule, check, collapse, collapseString, createReadme as create, createReadmeInteractive as createInteractive, defineConfig, expand, expandString, getContextMetadata, getReadmeMetadata, loadConfig, mergeConfig, setLogger };
|
|
183
|
+
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, strip, stripString };
|