mdat 2.3.6 → 3.0.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 +98 -85
- package/dist/lib/index.d.ts +0 -1
- package/dist/lib/index.js +67 -54
- package/package.json +24 -30
- package/readme.md +2 -4
package/dist/bin/cli.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createLogger, getChildLogger, injectionHelper } from "lognow";
|
|
2
3
|
import picocolors from "picocolors";
|
|
3
4
|
import prettyMilliseconds from "pretty-ms";
|
|
4
5
|
import { getMdatReports, getSoleRule, mdatCollapse, mdatDiff, mdatExpand, mdatSplit, mdatStrip, reporterMdat, rulesSchema, setLogger } from "remark-mdat";
|
|
5
|
-
import { createLogger, getChildLogger, injectionHelper } from "lognow";
|
|
6
|
-
import { defineTemplate, getMetadata, helpers, setLogger as setLogger$1, templates } from "metascope";
|
|
7
6
|
import { read, write } from "to-vfile";
|
|
8
7
|
import yargs from "yargs";
|
|
9
8
|
import { hideBin } from "yargs/helpers";
|
|
@@ -15,6 +14,7 @@ import fs from "node:fs/promises";
|
|
|
15
14
|
import path from "node:path";
|
|
16
15
|
import plur from "plur";
|
|
17
16
|
import { deepmerge } from "deepmerge-ts";
|
|
17
|
+
import { defineTemplate, getMetadata, helpers, setLogger as setLogger$1, templates } from "metascope";
|
|
18
18
|
import { z } from "zod";
|
|
19
19
|
import { globby } from "globby";
|
|
20
20
|
import { isFile, isFileSync } from "path-type";
|
|
@@ -29,7 +29,20 @@ import { confirm, group, intro, note, outro, select } from "@clack/prompts";
|
|
|
29
29
|
|
|
30
30
|
//#region package.json
|
|
31
31
|
var name = "mdat";
|
|
32
|
-
var version = "
|
|
32
|
+
var version = "3.0.0";
|
|
33
|
+
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/lib/deep-merge-defined.ts
|
|
36
|
+
function stripUndefinedDeep(object) {
|
|
37
|
+
if (Array.isArray(object)) return object.map((v) => v !== null && typeof v === "object" ? stripUndefinedDeep(v) : v).filter((v) => v !== void 0);
|
|
38
|
+
return Object.entries(object).map(([k, v]) => [k, v !== null && typeof v === "object" ? stripUndefinedDeep(v) : v]).reduce((acc, [k, v]) => v === void 0 ? acc : {
|
|
39
|
+
...acc,
|
|
40
|
+
[k]: v
|
|
41
|
+
}, {});
|
|
42
|
+
}
|
|
43
|
+
function deepMergeDefined(...objects) {
|
|
44
|
+
return deepmerge(...objects.map((v, i) => i === 0 ? v : stripUndefinedDeep(v)));
|
|
45
|
+
}
|
|
33
46
|
|
|
34
47
|
//#endregion
|
|
35
48
|
//#region src/lib/log.ts
|
|
@@ -55,19 +68,6 @@ function setLogger$2(logger) {
|
|
|
55
68
|
setLogger$1(getChildLogger(log, "metascope"));
|
|
56
69
|
}
|
|
57
70
|
|
|
58
|
-
//#endregion
|
|
59
|
-
//#region src/lib/deep-merge-defined.ts
|
|
60
|
-
function stripUndefinedDeep(object) {
|
|
61
|
-
if (Array.isArray(object)) return object.map((v) => v && typeof v === "object" ? stripUndefinedDeep(v) : v).filter((v) => v !== void 0);
|
|
62
|
-
return Object.entries(object).map(([k, v]) => [k, v && typeof v === "object" ? stripUndefinedDeep(v) : v]).reduce((acc, [k, v]) => v === void 0 ? acc : {
|
|
63
|
-
...acc,
|
|
64
|
-
[k]: v
|
|
65
|
-
}, {});
|
|
66
|
-
}
|
|
67
|
-
function deepMergeDefined(...objects) {
|
|
68
|
-
return deepmerge(...objects.map((v, i) => i === 0 ? v : stripUndefinedDeep(v)));
|
|
69
|
-
}
|
|
70
|
-
|
|
71
71
|
//#endregion
|
|
72
72
|
//#region src/lib/mdat-json-loader.ts
|
|
73
73
|
/**
|
|
@@ -82,7 +82,7 @@ function mdatJsonLoader(filePath, content) {
|
|
|
82
82
|
}
|
|
83
83
|
function flattenJson(jsonObject, parentKey = "", result = {}) {
|
|
84
84
|
for (const [key, value] of Object.entries(jsonObject)) {
|
|
85
|
-
const fullPath = parentKey ? `${parentKey}.${key}
|
|
85
|
+
const fullPath = parentKey === "" ? key : `${parentKey}.${key}`;
|
|
86
86
|
if (typeof value === "object" && value !== null && !Array.isArray(value)) flattenJson(value, fullPath, result);
|
|
87
87
|
else if (value === null) result[fullPath] = "null";
|
|
88
88
|
else result[fullPath] = value.toString();
|
|
@@ -137,16 +137,16 @@ async function getContextMetadata() {
|
|
|
137
137
|
});
|
|
138
138
|
return metascopeMetadata;
|
|
139
139
|
}
|
|
140
|
-
const GIT_PREFIX_REGEX = /^git
|
|
141
|
-
const GIT_SUFFIX_REGEX = /\.git
|
|
142
|
-
const TRAILING_SLASH_REGEX =
|
|
140
|
+
const GIT_PREFIX_REGEX = /^git\+/v;
|
|
141
|
+
const GIT_SUFFIX_REGEX = /\.git$/v;
|
|
142
|
+
const TRAILING_SLASH_REGEX = /\/$/v;
|
|
143
143
|
const readmeMetadataTemplate = defineTemplate((context) => {
|
|
144
144
|
const { githubActions, licenseFile, metascope, nodePackageJson } = context;
|
|
145
145
|
const codemeta = templates.codemetaJson(context, {});
|
|
146
146
|
const nodePackage = helpers.firstOf(nodePackageJson)?.data;
|
|
147
147
|
const licenseFileData = helpers.firstOf(licenseFile);
|
|
148
148
|
const ciActionFilePath = helpers.ensureArray(githubActions).find((entry) => entry.data.name.toLowerCase() === "ci")?.source;
|
|
149
|
-
const
|
|
149
|
+
const repoUrl = codemeta.codeRepository?.replace(GIT_PREFIX_REGEX, "").replace(GIT_SUFFIX_REGEX, "").replace(TRAILING_SLASH_REGEX, "");
|
|
150
150
|
const bin = (() => {
|
|
151
151
|
if (nodePackage === void 0) return;
|
|
152
152
|
const binField = nodePackage.bin;
|
|
@@ -179,7 +179,7 @@ const readmeMetadataTemplate = defineTemplate((context) => {
|
|
|
179
179
|
author: helpers.firstOf(helpers.mixedStringsToArray(helpers.toBasicNames(codemeta.author))),
|
|
180
180
|
authorUrl: firstAuthor?.url,
|
|
181
181
|
bin,
|
|
182
|
-
ciActionFileName: ciActionFilePath ? path.basename(ciActionFilePath)
|
|
182
|
+
ciActionFileName: ciActionFilePath === void 0 ? void 0 : path.basename(ciActionFilePath),
|
|
183
183
|
description: codemeta.description,
|
|
184
184
|
engines,
|
|
185
185
|
isPublicNpmPackage: nodePackage?.private !== true && (nodePackage?.name.startsWith("@") && nodePackage.publishConfig?.access === "public" || true),
|
|
@@ -191,7 +191,7 @@ const readmeMetadataTemplate = defineTemplate((context) => {
|
|
|
191
191
|
operatingSystem: codemeta.operatingSystem,
|
|
192
192
|
peerDependencies,
|
|
193
193
|
projectDirectory: metascope?.data.options.path === void 0 ? void 0 : `file://${metascope.data.options.path}`,
|
|
194
|
-
repositoryUrl,
|
|
194
|
+
repositoryUrl: repoUrl,
|
|
195
195
|
runtimePlatform: codemeta.runtimePlatform,
|
|
196
196
|
usesPnpm: helpers.usesPnpm(nodePackageJson)
|
|
197
197
|
};
|
|
@@ -204,7 +204,8 @@ let readmeMetadata;
|
|
|
204
204
|
*/
|
|
205
205
|
async function getReadmeMetadata() {
|
|
206
206
|
if (readmeMetadata !== void 0) return readmeMetadata;
|
|
207
|
-
|
|
207
|
+
const contextMetadata = await getContextMetadata();
|
|
208
|
+
readmeMetadata = readmeMetadataTemplate(contextMetadata, {});
|
|
208
209
|
return readmeMetadata;
|
|
209
210
|
}
|
|
210
211
|
|
|
@@ -222,11 +223,11 @@ var badges_default = { badges: { async content(options) {
|
|
|
222
223
|
const { ciActionFileName, license, licenseUrl, name, repositoryUrl } = metadata;
|
|
223
224
|
const badges = [];
|
|
224
225
|
if (validOptions?.npm === void 0) {
|
|
225
|
-
if (metadata.isPublicNpmPackage) badges.push(`[](https://npmjs.com/package/${name})`);
|
|
226
|
-
} else for (const
|
|
226
|
+
if (metadata.isPublicNpmPackage) badges.push(`[](https://www.npmjs.com/package/${name})`);
|
|
227
|
+
} else for (const packageName of validOptions.npm) badges.push(`[](https://www.npmjs.com/package/${packageName})`);
|
|
227
228
|
if (license !== void 0 && licenseUrl !== void 0) badges.push(`[}-yellow.svg)](${licenseUrl})`);
|
|
228
229
|
if (ciActionFileName !== void 0 && repositoryUrl !== void 0) badges.push(`[](${repositoryUrl}/actions/workflows/${ciActionFileName})`);
|
|
229
|
-
if (validOptions?.custom !== void 0) for (const [
|
|
230
|
+
if (validOptions?.custom !== void 0) for (const [badgeName, { image, link }] of Object.entries(validOptions.custom)) badges.push(`[](${link})`);
|
|
230
231
|
return badges.join("\n");
|
|
231
232
|
} } };
|
|
232
233
|
|
|
@@ -239,7 +240,7 @@ var banner_default = { banner: { async content(options) {
|
|
|
239
240
|
}).optional().parse(options);
|
|
240
241
|
const src = validOptions?.src ?? await getBannerSrc();
|
|
241
242
|
if (src === void 0) throw new Error("Banner image not found at any typical location, consider adding something at ./assets/banner.webp");
|
|
242
|
-
|
|
243
|
+
if (!isUrl(src) && !await isFile(src)) throw new Error(`Banner image not found at "${src}"`);
|
|
243
244
|
let alt = validOptions?.alt;
|
|
244
245
|
if (alt === void 0) {
|
|
245
246
|
const { name: packageName } = await getReadmeMetadata();
|
|
@@ -251,7 +252,7 @@ var banner_default = { banner: { async content(options) {
|
|
|
251
252
|
async function getBannerSrc() {
|
|
252
253
|
const { projectDirectory } = await getReadmeMetadata();
|
|
253
254
|
if (projectDirectory === void 0) throw new Error("No project directory found");
|
|
254
|
-
const
|
|
255
|
+
const [firstPath] = await globby([
|
|
255
256
|
".",
|
|
256
257
|
"assets",
|
|
257
258
|
"media",
|
|
@@ -287,20 +288,19 @@ async function getBannerSrc() {
|
|
|
287
288
|
]
|
|
288
289
|
}
|
|
289
290
|
});
|
|
290
|
-
|
|
291
|
+
return firstPath === void 0 ? void 0 : path.relative(process.cwd(), firstPath);
|
|
291
292
|
}
|
|
292
293
|
function isUrl(text, lenient = true) {
|
|
293
294
|
if (typeof text !== "string") throw new TypeError("Expected a string");
|
|
294
295
|
text = text.trim();
|
|
295
296
|
if (text.includes(" ")) return false;
|
|
296
297
|
if (URL.canParse(text)) return true;
|
|
297
|
-
|
|
298
|
-
return false;
|
|
298
|
+
return lenient && URL.canParse(`https://${text}`);
|
|
299
299
|
}
|
|
300
300
|
|
|
301
301
|
//#endregion
|
|
302
302
|
//#region src/lib/readme/rules/code.ts
|
|
303
|
-
const LEADING_DOT_REGEX =
|
|
303
|
+
const LEADING_DOT_REGEX = /^\./v;
|
|
304
304
|
var code_default = { code: { async content(options) {
|
|
305
305
|
const validOptions = z.object({
|
|
306
306
|
file: z.string(),
|
|
@@ -379,7 +379,7 @@ var dependencies_default = { dependencies: { async content() {
|
|
|
379
379
|
if (engines?.[platformKey] !== void 0) continue;
|
|
380
380
|
const info = PLATFORM_INFO[platformKey.toLowerCase()];
|
|
381
381
|
const display = info ? `[${info.display}](${info.url})` : platformKey;
|
|
382
|
-
platformItems.push(version ? `- ${display}
|
|
382
|
+
platformItems.push(version === void 0 || version === "" ? `- ${display}` : `- ${display} ${version}`);
|
|
383
383
|
}
|
|
384
384
|
if (operatingSystem !== void 0 && operatingSystem.length > 0) platformItems.push(`- Supported platforms: ${operatingSystem.join(", ")}`);
|
|
385
385
|
const peerItems = [];
|
|
@@ -446,7 +446,7 @@ var title_default = { title: {
|
|
|
446
446
|
},
|
|
447
447
|
order: 2
|
|
448
448
|
} };
|
|
449
|
-
const SPLIT_FOR_TITLE_CASE_REGEX = /[ _
|
|
449
|
+
const SPLIT_FOR_TITLE_CASE_REGEX = /[ _\-]+/v;
|
|
450
450
|
function makeTitleCase(text) {
|
|
451
451
|
return text.split(SPLIT_FOR_TITLE_CASE_REGEX).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
452
452
|
}
|
|
@@ -520,7 +520,7 @@ async function createSizeReport(filePath) {
|
|
|
520
520
|
original: createSizeInfo(originalSize, originalSize)
|
|
521
521
|
};
|
|
522
522
|
} catch (error) {
|
|
523
|
-
throw new Error(`Failed to analyze file: ${error instanceof Error ? error.message : "Unknown error"}
|
|
523
|
+
throw new Error(`Failed to analyze file: ${error instanceof Error ? error.message : "Unknown error"}`, { cause: error });
|
|
524
524
|
}
|
|
525
525
|
}
|
|
526
526
|
|
|
@@ -618,8 +618,8 @@ var table_of_contents_default = { "table-of-contents": {
|
|
|
618
618
|
]).optional() }).optional().parse(options)?.depth ?? 3,
|
|
619
619
|
tight: true
|
|
620
620
|
});
|
|
621
|
-
const heading = `## Table of contents`;
|
|
622
621
|
if (result.map === void 0) throw new Error("Could not generate table of contents");
|
|
622
|
+
const heading = `## Table of contents`;
|
|
623
623
|
const rootWrapper = {
|
|
624
624
|
children: result.map.children,
|
|
625
625
|
type: "root"
|
|
@@ -680,13 +680,13 @@ function getAdditionalConfigExplorer() {
|
|
|
680
680
|
async function loadConfig(options) {
|
|
681
681
|
const { additionalConfig, defaults = rules_default, searchFrom } = options ?? {};
|
|
682
682
|
let finalConfig = { mdat: `Powered by the Markdown Autophagic Template system: [mdat](https://github.com/kitschpatrol/mdat).` };
|
|
683
|
-
|
|
683
|
+
finalConfig = deepMergeDefined(finalConfig, defaults);
|
|
684
684
|
const results = await getConfigExplorer().search(searchFrom);
|
|
685
685
|
if (results) {
|
|
686
686
|
const { config, filepath } = results;
|
|
687
687
|
let possibleRules = config;
|
|
688
688
|
log.debug(`Using config from "${filepath}"`);
|
|
689
|
-
if (filepath.endsWith("package.json")
|
|
689
|
+
if (typeof config === "string" && filepath.endsWith("package.json")) {
|
|
690
690
|
log.debug(`Detected shared config string: "${config}"`);
|
|
691
691
|
const { default: sharedConfig } = await import(config);
|
|
692
692
|
possibleRules = sharedConfig;
|
|
@@ -699,14 +699,14 @@ async function loadConfig(options) {
|
|
|
699
699
|
for (const configOrPath of additionalConfigArray) {
|
|
700
700
|
let loaded;
|
|
701
701
|
if (typeof configOrPath === "string") {
|
|
702
|
-
let
|
|
703
|
-
if (path.basename(configOrPath).endsWith("package.json"))
|
|
702
|
+
let loadResults;
|
|
703
|
+
if (path.basename(configOrPath).endsWith("package.json")) loadResults = {
|
|
704
704
|
config: mdatJsonLoader(configOrPath, await fs.readFile(configOrPath, "utf8")),
|
|
705
705
|
filepath: configOrPath
|
|
706
706
|
};
|
|
707
|
-
else
|
|
708
|
-
if (
|
|
709
|
-
const { config: loadedConfig, filepath } =
|
|
707
|
+
else loadResults = await getAdditionalConfigExplorer().load(configOrPath);
|
|
708
|
+
if (loadResults === null || loadResults === void 0) continue;
|
|
709
|
+
const { config: loadedConfig, filepath } = loadResults;
|
|
710
710
|
log.debug(`Loaded additional config from "${filepath}"`);
|
|
711
711
|
loaded = loadedConfig;
|
|
712
712
|
} else loaded = configOrPath;
|
|
@@ -740,7 +740,7 @@ async function formatWithPrettier(content, filePath) {
|
|
|
740
740
|
} catch {
|
|
741
741
|
throw new Error("The --format flag requires `prettier` to be installed. Run: pnpm add -D prettier");
|
|
742
742
|
}
|
|
743
|
-
const configKey = filePath ?
|
|
743
|
+
const configKey = filePath === void 0 || filePath === "" ? process.cwd() : path.dirname(filePath);
|
|
744
744
|
let config = configCache.get(configKey);
|
|
745
745
|
if (config === void 0 && !configCache.has(configKey)) {
|
|
746
746
|
config = await cachedPrettier.resolveConfig(filePath ?? process.cwd());
|
|
@@ -763,22 +763,22 @@ function zeroPad(n, nMax) {
|
|
|
763
763
|
async function getInputOutputPaths(inputs, output, name, extension) {
|
|
764
764
|
const paths = [];
|
|
765
765
|
for (const [index, file] of inputs.entries()) {
|
|
766
|
-
const inputOutputPath = await getInputOutputPath(file, output, name, extension, name && inputs.length > 1 ? `-${zeroPad(index + 1, inputs.length)}` : "");
|
|
766
|
+
const inputOutputPath = await getInputOutputPath(file, output, name, extension, name !== void 0 && name !== "" && inputs.length > 1 ? `-${zeroPad(index + 1, inputs.length)}` : "");
|
|
767
767
|
paths.push(inputOutputPath);
|
|
768
768
|
}
|
|
769
769
|
return paths;
|
|
770
770
|
}
|
|
771
771
|
async function getInputOutputPath(input, output, name, extension, nameSuffix = "") {
|
|
772
772
|
const resolvedInput = expandPath(input);
|
|
773
|
-
const resolvedOutput = output ? expandPath(output) : void 0;
|
|
774
773
|
if (!isFileSync(resolvedInput)) throw new Error(`Input file not found: "${resolvedInput}"`);
|
|
775
|
-
|
|
774
|
+
const resolvedOutput = output === void 0 || output === "" ? void 0 : expandPath(output);
|
|
775
|
+
if (resolvedOutput !== void 0) {
|
|
776
776
|
if (isFileSync(resolvedOutput)) throw new Error(`Output path must be a directory, received a file path: "${resolvedOutput}"`);
|
|
777
777
|
await fs.mkdir(resolvedOutput, { recursive: true });
|
|
778
778
|
}
|
|
779
779
|
return {
|
|
780
780
|
input: resolvedInput,
|
|
781
|
-
name: `${name ? path.basename(
|
|
781
|
+
name: `${name === void 0 || name === "" ? path.basename(resolvedInput, path.extname(resolvedInput)) : path.basename(name, path.extname(name))}${nameSuffix}${extension === void 0 ? name !== void 0 && path.extname(name) !== "" ? path.extname(name) : path.extname(input) : `.${extension}`}`,
|
|
782
782
|
output: resolvedOutput ?? path.dirname(resolvedInput)
|
|
783
783
|
};
|
|
784
784
|
}
|
|
@@ -789,7 +789,7 @@ function ensureArray(value) {
|
|
|
789
789
|
if (value === void 0 || value === null) return [];
|
|
790
790
|
return Array.isArray(value) ? value : [value];
|
|
791
791
|
}
|
|
792
|
-
const README_SEARCH_REGEX = /^readme(?:\.\w+)?$/
|
|
792
|
+
const README_SEARCH_REGEX = /^readme(?:\.\w+)?$/iv;
|
|
793
793
|
/**
|
|
794
794
|
* Finds a readme file in the current working directory (case-insensitive).
|
|
795
795
|
*/
|
|
@@ -866,14 +866,14 @@ function stripLintPlugins(config) {
|
|
|
866
866
|
//#endregion
|
|
867
867
|
//#region src/lib/processors.ts
|
|
868
868
|
async function processFiles(files, loader, processorGetter, name, output, config) {
|
|
869
|
-
const [resolvedConfig,
|
|
869
|
+
const [resolvedConfig, localRemarkConfig] = await Promise.all([loader({ additionalConfig: config }), loadAmbientRemarkConfig()]);
|
|
870
870
|
const inputOutputPaths = await getInputOutputPaths(ensureArray(files), output, name, "md");
|
|
871
|
-
const resolvedProcessor = processorGetter(resolvedConfig,
|
|
872
|
-
return await Promise.all(inputOutputPaths.map(async ({ input, name, output }) => {
|
|
871
|
+
const resolvedProcessor = processorGetter(resolvedConfig, localRemarkConfig);
|
|
872
|
+
return await Promise.all(inputOutputPaths.map(async ({ input, name: outputName, output: outputDirectory }) => {
|
|
873
873
|
const inputFile = await read(input);
|
|
874
874
|
const result = await resolvedProcessor.process(inputFile);
|
|
875
|
-
result.dirname =
|
|
876
|
-
result.basename =
|
|
875
|
+
result.dirname = outputDirectory;
|
|
876
|
+
result.basename = outputName;
|
|
877
877
|
return result;
|
|
878
878
|
}));
|
|
879
879
|
}
|
|
@@ -970,13 +970,15 @@ async function createReadmeInteractive() {
|
|
|
970
970
|
const readmePath = await findReadme();
|
|
971
971
|
intro(`Running ${picocolors.bold("mdat create")} interactively`);
|
|
972
972
|
const newReadmePath = await createReadme(await group({
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
973
|
+
async overwrite() {
|
|
974
|
+
if (readmePath === void 0) return true;
|
|
975
|
+
if (await confirm({
|
|
976
|
+
message: `Found an existing readme at "${picocolors.blue(readmePath)}". Do you want to overwrite it?`,
|
|
977
|
+
active: "Overwrite",
|
|
978
|
+
inactive: "Exit"
|
|
979
|
+
}) === false) throw new Error("`mdat create` was cancelled to avoid an overwrite - no changes were made");
|
|
980
|
+
return true;
|
|
981
|
+
},
|
|
980
982
|
template: async () => select({
|
|
981
983
|
message: "Which template would you like to use?",
|
|
982
984
|
options: getTemplateOptions()
|
|
@@ -1018,17 +1020,24 @@ async function createReadme(options) {
|
|
|
1018
1020
|
const templateString = getTemplateForConfig(resolvedOptions.template, resolvedOptions.compound);
|
|
1019
1021
|
const readmePath = path.join(resolvedOptions.output, "readme.md");
|
|
1020
1022
|
if (!resolvedOptions.overwrite) {
|
|
1021
|
-
|
|
1023
|
+
let exists = true;
|
|
1024
|
+
try {
|
|
1025
|
+
await fs.access(readmePath);
|
|
1026
|
+
} catch {
|
|
1027
|
+
exists = false;
|
|
1028
|
+
}
|
|
1029
|
+
if (exists) throw new Error(`Readme already exists at "${readmePath}". Use the overwrite option to replace it.`);
|
|
1022
1030
|
}
|
|
1023
1031
|
await fs.writeFile(readmePath, templateString, "utf8");
|
|
1024
1032
|
if (resolvedOptions.expand) {
|
|
1025
1033
|
const [result] = await expand(readmePath);
|
|
1034
|
+
if (result === void 0) throw new Error(`Failed to expand readme at "${readmePath}"`);
|
|
1026
1035
|
await write(result);
|
|
1027
1036
|
}
|
|
1028
1037
|
return readmePath;
|
|
1029
1038
|
}
|
|
1030
1039
|
function getTemplateForConfig(templateKey, compound) {
|
|
1031
|
-
if (!(templateKey
|
|
1040
|
+
if (!Object.hasOwn(templates_default, templateKey)) throw new Error(`Unknown template "${templateKey}". Available templates: ${Object.keys(templates_default).join(", ")}`);
|
|
1032
1041
|
return templates_default[templateKey].content[compound ? "compound" : "explicit"];
|
|
1033
1042
|
}
|
|
1034
1043
|
function getTemplateOptions() {
|
|
@@ -1093,7 +1102,11 @@ async function check(files, config, options) {
|
|
|
1093
1102
|
const resolvedFiles = Array.isArray(files) ? files : [files];
|
|
1094
1103
|
const [originals, results] = await Promise.all([Promise.all(resolvedFiles.map(async (f) => read(f))), processFiles(files, loadConfig, getExpandProcessor, void 0, void 0, config)]);
|
|
1095
1104
|
if (options?.format) await formatResults(results);
|
|
1096
|
-
return results.map((result, i) =>
|
|
1105
|
+
return results.map((result, i) => {
|
|
1106
|
+
const original = originals[i];
|
|
1107
|
+
if (original === void 0) throw new Error(`Missing original file for comparison at index ${i}`);
|
|
1108
|
+
return compareWithDiff(original, result, options);
|
|
1109
|
+
});
|
|
1097
1110
|
}
|
|
1098
1111
|
function compareWithDiff(original, result, options) {
|
|
1099
1112
|
const inSync = original.toString().replaceAll("\r\n", "\n") === result.toString();
|
|
@@ -1115,7 +1128,7 @@ function compareWithDiff(original, result, options) {
|
|
|
1115
1128
|
};
|
|
1116
1129
|
}
|
|
1117
1130
|
async function formatResults(results) {
|
|
1118
|
-
for (const file of results) file.value = await formatWithPrettier(file.toString(), file.path
|
|
1131
|
+
for (const file of results) file.value = await formatWithPrettier(file.toString(), file.path === "" ? void 0 : file.path);
|
|
1119
1132
|
}
|
|
1120
1133
|
|
|
1121
1134
|
//#endregion
|
|
@@ -1195,55 +1208,55 @@ const yargsInstance = yargs(hideBin(process.argv));
|
|
|
1195
1208
|
try {
|
|
1196
1209
|
await yargsInstance.scriptName("mdat").usage("$0 [command] [files..] [options]", "Work with MDAT placeholder comments in Markdown files.").option(verboseOption).middleware((argv) => {
|
|
1197
1210
|
setLogger$2(createLogger({
|
|
1198
|
-
name,
|
|
1199
|
-
verbose: argv.verbose ?? false,
|
|
1200
1211
|
logToConsole: {
|
|
1201
1212
|
showLevel: false,
|
|
1202
1213
|
showName: false,
|
|
1203
1214
|
showTime: false
|
|
1204
|
-
}
|
|
1215
|
+
},
|
|
1216
|
+
name,
|
|
1217
|
+
verbose: argv.verbose ?? false
|
|
1205
1218
|
}));
|
|
1206
|
-
}).command(["$0 [files..] [options]", "expand [files..] [options]"], "Expand MDAT placeholder comments. If no files are provided, the closest readme.md is expanded.", (
|
|
1219
|
+
}).command(["$0 [files..] [options]", "expand [files..] [options]"], "Expand MDAT placeholder comments. If no files are provided, the closest readme.md is expanded.", (commandYargs) => commandYargs.positional(...filesPositional).option(configOption).option(outputOption).option(nameOption).option(printOption).option(formatOption), async ({ config, files, format, name: fileName, output, print }) => {
|
|
1207
1220
|
logConflicts({
|
|
1208
|
-
name,
|
|
1221
|
+
name: fileName,
|
|
1209
1222
|
output,
|
|
1210
1223
|
print
|
|
1211
1224
|
});
|
|
1212
|
-
const results = await expand(files,
|
|
1225
|
+
const results = await expand(files, fileName, output, collectConfig(config), { format });
|
|
1213
1226
|
for (const file of results) if (print) process.stdout.write(file.toString());
|
|
1214
1227
|
else await write(file);
|
|
1215
1228
|
reporterMdat(results);
|
|
1216
1229
|
log.debug(`Expanded comments in ${prettyMilliseconds(performance.now() - startTime)}.`);
|
|
1217
1230
|
process.exitCode = getExitCode(results);
|
|
1218
|
-
}).command("collapse [files..] [options]", "Collapse MDAT placeholder comments. If no files are provided, the closest readme.md is collapsed.", (
|
|
1231
|
+
}).command("collapse [files..] [options]", "Collapse MDAT placeholder comments. If no files are provided, the closest readme.md is collapsed.", (commandYargs) => commandYargs.positional(...filesPositional).option(outputOption).option(nameOption).option(printOption).option(formatOption), async ({ files, format, name: fileName, output, print }) => {
|
|
1219
1232
|
logConflicts({
|
|
1220
|
-
name,
|
|
1233
|
+
name: fileName,
|
|
1221
1234
|
output,
|
|
1222
1235
|
print
|
|
1223
1236
|
});
|
|
1224
|
-
const results = await collapse(files,
|
|
1237
|
+
const results = await collapse(files, fileName, output, void 0, { format });
|
|
1225
1238
|
for (const file of results) if (print) process.stdout.write(file.toString());
|
|
1226
1239
|
else await write(file);
|
|
1227
1240
|
reporterMdat(results);
|
|
1228
1241
|
log.debug(`Collapsed comments in ${prettyMilliseconds(performance.now() - startTime)}.`);
|
|
1229
1242
|
process.exitCode = getExitCode(results);
|
|
1230
|
-
}).command("strip [files..] [options]", "Strip MDAT comments while preserving expanded content. If no files are provided, the closest readme.md is stripped.", (
|
|
1243
|
+
}).command("strip [files..] [options]", "Strip MDAT comments while preserving expanded content. If no files are provided, the closest readme.md is stripped.", (commandYargs) => commandYargs.positional(...filesPositional).option(outputOption).option(nameOption).option(printOption).option(formatOption), async ({ files, format, name: fileName, output, print }) => {
|
|
1231
1244
|
logConflicts({
|
|
1232
|
-
name,
|
|
1245
|
+
name: fileName,
|
|
1233
1246
|
output,
|
|
1234
1247
|
print
|
|
1235
1248
|
});
|
|
1236
|
-
const results = await strip(files,
|
|
1249
|
+
const results = await strip(files, fileName, output, void 0, { format });
|
|
1237
1250
|
for (const file of results) if (print) process.stdout.write(file.toString());
|
|
1238
1251
|
else await write(file);
|
|
1239
1252
|
reporterMdat(results);
|
|
1240
1253
|
log.debug(`Stripped comments in ${prettyMilliseconds(performance.now() - startTime)}.`);
|
|
1241
1254
|
process.exitCode = getExitCode(results);
|
|
1242
|
-
}).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.", (
|
|
1255
|
+
}).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.", (commandYargs) => commandYargs.positional(...filesPositional).option(configOption).option(formatOption), async ({ config, files, format }) => {
|
|
1243
1256
|
const results = await check(files, collectConfig(config), { format });
|
|
1244
1257
|
let allInSync = true;
|
|
1245
1258
|
for (const { inSync, result } of results) {
|
|
1246
|
-
const filePath = result.path
|
|
1259
|
+
const filePath = result.path === "" ? "unknown" : result.path;
|
|
1247
1260
|
if (inSync) log.debug(`${picocolors.green("Up to date")}: ${filePath}`);
|
|
1248
1261
|
else {
|
|
1249
1262
|
log.warn(`${picocolors.red("Stale content")}: ${filePath}`);
|
|
@@ -1253,12 +1266,12 @@ try {
|
|
|
1253
1266
|
}
|
|
1254
1267
|
log.debug(`Checked in ${prettyMilliseconds(performance.now() - startTime)}.`);
|
|
1255
1268
|
process.exitCode = allInSync ? 0 : 1;
|
|
1256
|
-
}).command("create [options]", "Create a new Markdown file from a template.", (
|
|
1269
|
+
}).command("create [options]", "Create a new Markdown file from a template.", (commandYargs) => commandYargs.option(interactiveOption).option(overwriteOption).option(outputOption).option(expandOption).option(templateOption).option(compoundOption), async ({ compound, expand: expandTemplate, interactive, output, overwrite, template }) => {
|
|
1257
1270
|
if (interactive) await createReadmeInteractive();
|
|
1258
1271
|
else {
|
|
1259
1272
|
const readmePath = await createReadme({
|
|
1260
1273
|
compound,
|
|
1261
|
-
expand,
|
|
1274
|
+
expand: expandTemplate,
|
|
1262
1275
|
output,
|
|
1263
1276
|
overwrite,
|
|
1264
1277
|
template
|
|
@@ -1272,11 +1285,11 @@ try {
|
|
|
1272
1285
|
process.exitCode = 1;
|
|
1273
1286
|
}
|
|
1274
1287
|
function logConflicts(args) {
|
|
1275
|
-
if (args.print && args.output) log.warn(`Ignoring --output option because --print is set`);
|
|
1276
|
-
if (args.print && args.name) log.warn(`Ignoring --name option because --print is set`);
|
|
1288
|
+
if (args.print && args.output !== void 0 && args.output !== "") log.warn(`Ignoring --output option because --print is set`);
|
|
1289
|
+
if (args.print && args.name !== void 0 && args.name !== "") log.warn(`Ignoring --name option because --print is set`);
|
|
1277
1290
|
}
|
|
1278
1291
|
function collectConfig(config) {
|
|
1279
|
-
if (config === void 0) return
|
|
1292
|
+
if (config === void 0) return;
|
|
1280
1293
|
return ensureArray(config);
|
|
1281
1294
|
}
|
|
1282
1295
|
function getExitCode(results) {
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { Rule, Rule as Rule$1 } from "remark-mdat";
|
|
|
2
2
|
import { ILogBasic, ILogLayer } from "lognow";
|
|
3
3
|
import { MetadataContext } from "metascope";
|
|
4
4
|
import { VFile } from "vfile";
|
|
5
|
-
|
|
6
5
|
//#region src/lib/config.d.ts
|
|
7
6
|
/**
|
|
8
7
|
* An mdat configuration is a record of expansion rules. Keys become comment
|
package/dist/lib/index.js
CHANGED
|
@@ -24,8 +24,8 @@ import untildify from "untildify";
|
|
|
24
24
|
import { confirm, group, intro, note, outro, select } from "@clack/prompts";
|
|
25
25
|
//#region src/lib/deep-merge-defined.ts
|
|
26
26
|
function stripUndefinedDeep(object) {
|
|
27
|
-
if (Array.isArray(object)) return object.map((v) => v && typeof v === "object" ? stripUndefinedDeep(v) : v).filter((v) => v !== void 0);
|
|
28
|
-
return Object.entries(object).map(([k, v]) => [k, v && typeof v === "object" ? stripUndefinedDeep(v) : v]).reduce((acc, [k, v]) => v === void 0 ? acc : {
|
|
27
|
+
if (Array.isArray(object)) return object.map((v) => v !== null && typeof v === "object" ? stripUndefinedDeep(v) : v).filter((v) => v !== void 0);
|
|
28
|
+
return Object.entries(object).map(([k, v]) => [k, v !== null && typeof v === "object" ? stripUndefinedDeep(v) : v]).reduce((acc, [k, v]) => v === void 0 ? acc : {
|
|
29
29
|
...acc,
|
|
30
30
|
[k]: v
|
|
31
31
|
}, {});
|
|
@@ -70,7 +70,7 @@ function mdatJsonLoader(filePath, content) {
|
|
|
70
70
|
}
|
|
71
71
|
function flattenJson(jsonObject, parentKey = "", result = {}) {
|
|
72
72
|
for (const [key, value] of Object.entries(jsonObject)) {
|
|
73
|
-
const fullPath = parentKey ? `${parentKey}.${key}
|
|
73
|
+
const fullPath = parentKey === "" ? key : `${parentKey}.${key}`;
|
|
74
74
|
if (typeof value === "object" && value !== null && !Array.isArray(value)) flattenJson(value, fullPath, result);
|
|
75
75
|
else if (value === null) result[fullPath] = "null";
|
|
76
76
|
else result[fullPath] = value.toString();
|
|
@@ -124,9 +124,9 @@ async function getContextMetadata() {
|
|
|
124
124
|
});
|
|
125
125
|
return metascopeMetadata;
|
|
126
126
|
}
|
|
127
|
-
const GIT_PREFIX_REGEX = /^git
|
|
128
|
-
const GIT_SUFFIX_REGEX = /\.git
|
|
129
|
-
const TRAILING_SLASH_REGEX =
|
|
127
|
+
const GIT_PREFIX_REGEX = /^git\+/v;
|
|
128
|
+
const GIT_SUFFIX_REGEX = /\.git$/v;
|
|
129
|
+
const TRAILING_SLASH_REGEX = /\/$/v;
|
|
130
130
|
/**
|
|
131
131
|
* Reset cached context metadata. Call between tests or when the underlying
|
|
132
132
|
* project files may have changed on disk.
|
|
@@ -142,7 +142,7 @@ const readmeMetadataTemplate = defineTemplate((context) => {
|
|
|
142
142
|
const nodePackage = helpers.firstOf(nodePackageJson)?.data;
|
|
143
143
|
const licenseFileData = helpers.firstOf(licenseFile);
|
|
144
144
|
const ciActionFilePath = helpers.ensureArray(githubActions).find((entry) => entry.data.name.toLowerCase() === "ci")?.source;
|
|
145
|
-
const
|
|
145
|
+
const repoUrl = codemeta.codeRepository?.replace(GIT_PREFIX_REGEX, "").replace(GIT_SUFFIX_REGEX, "").replace(TRAILING_SLASH_REGEX, "");
|
|
146
146
|
const bin = (() => {
|
|
147
147
|
if (nodePackage === void 0) return;
|
|
148
148
|
const binField = nodePackage.bin;
|
|
@@ -175,7 +175,7 @@ const readmeMetadataTemplate = defineTemplate((context) => {
|
|
|
175
175
|
author: helpers.firstOf(helpers.mixedStringsToArray(helpers.toBasicNames(codemeta.author))),
|
|
176
176
|
authorUrl: firstAuthor?.url,
|
|
177
177
|
bin,
|
|
178
|
-
ciActionFileName: ciActionFilePath ? path.basename(ciActionFilePath)
|
|
178
|
+
ciActionFileName: ciActionFilePath === void 0 ? void 0 : path.basename(ciActionFilePath),
|
|
179
179
|
description: codemeta.description,
|
|
180
180
|
engines,
|
|
181
181
|
isPublicNpmPackage: nodePackage?.private !== true && (nodePackage?.name.startsWith("@") && nodePackage.publishConfig?.access === "public" || true),
|
|
@@ -187,7 +187,7 @@ const readmeMetadataTemplate = defineTemplate((context) => {
|
|
|
187
187
|
operatingSystem: codemeta.operatingSystem,
|
|
188
188
|
peerDependencies,
|
|
189
189
|
projectDirectory: metascope?.data.options.path === void 0 ? void 0 : `file://${metascope.data.options.path}`,
|
|
190
|
-
repositoryUrl,
|
|
190
|
+
repositoryUrl: repoUrl,
|
|
191
191
|
runtimePlatform: codemeta.runtimePlatform,
|
|
192
192
|
usesPnpm: helpers.usesPnpm(nodePackageJson)
|
|
193
193
|
};
|
|
@@ -200,7 +200,8 @@ let readmeMetadata;
|
|
|
200
200
|
*/
|
|
201
201
|
async function getReadmeMetadata() {
|
|
202
202
|
if (readmeMetadata !== void 0) return readmeMetadata;
|
|
203
|
-
|
|
203
|
+
const contextMetadata = await getContextMetadata();
|
|
204
|
+
readmeMetadata = readmeMetadataTemplate(contextMetadata, {});
|
|
204
205
|
return readmeMetadata;
|
|
205
206
|
}
|
|
206
207
|
/**
|
|
@@ -234,11 +235,11 @@ var badges_default = { badges: { async content(options) {
|
|
|
234
235
|
const { ciActionFileName, license, licenseUrl, name, repositoryUrl } = metadata;
|
|
235
236
|
const badges = [];
|
|
236
237
|
if (validOptions?.npm === void 0) {
|
|
237
|
-
if (metadata.isPublicNpmPackage) badges.push(`[](https://npmjs.com/package/${name})`);
|
|
238
|
-
} else for (const
|
|
238
|
+
if (metadata.isPublicNpmPackage) badges.push(`[](https://www.npmjs.com/package/${name})`);
|
|
239
|
+
} else for (const packageName of validOptions.npm) badges.push(`[](https://www.npmjs.com/package/${packageName})`);
|
|
239
240
|
if (license !== void 0 && licenseUrl !== void 0) badges.push(`[}-yellow.svg)](${licenseUrl})`);
|
|
240
241
|
if (ciActionFileName !== void 0 && repositoryUrl !== void 0) badges.push(`[](${repositoryUrl}/actions/workflows/${ciActionFileName})`);
|
|
241
|
-
if (validOptions?.custom !== void 0) for (const [
|
|
242
|
+
if (validOptions?.custom !== void 0) for (const [badgeName, { image, link }] of Object.entries(validOptions.custom)) badges.push(`[](${link})`);
|
|
242
243
|
return badges.join("\n");
|
|
243
244
|
} } };
|
|
244
245
|
//#endregion
|
|
@@ -250,7 +251,7 @@ var banner_default = { banner: { async content(options) {
|
|
|
250
251
|
}).optional().parse(options);
|
|
251
252
|
const src = validOptions?.src ?? await getBannerSrc();
|
|
252
253
|
if (src === void 0) throw new Error("Banner image not found at any typical location, consider adding something at ./assets/banner.webp");
|
|
253
|
-
|
|
254
|
+
if (!isUrl(src) && !await isFile(src)) throw new Error(`Banner image not found at "${src}"`);
|
|
254
255
|
let alt = validOptions?.alt;
|
|
255
256
|
if (alt === void 0) {
|
|
256
257
|
const { name: packageName } = await getReadmeMetadata();
|
|
@@ -262,7 +263,7 @@ var banner_default = { banner: { async content(options) {
|
|
|
262
263
|
async function getBannerSrc() {
|
|
263
264
|
const { projectDirectory } = await getReadmeMetadata();
|
|
264
265
|
if (projectDirectory === void 0) throw new Error("No project directory found");
|
|
265
|
-
const
|
|
266
|
+
const [firstPath] = await globby([
|
|
266
267
|
".",
|
|
267
268
|
"assets",
|
|
268
269
|
"media",
|
|
@@ -298,19 +299,18 @@ async function getBannerSrc() {
|
|
|
298
299
|
]
|
|
299
300
|
}
|
|
300
301
|
});
|
|
301
|
-
|
|
302
|
+
return firstPath === void 0 ? void 0 : path.relative(process.cwd(), firstPath);
|
|
302
303
|
}
|
|
303
304
|
function isUrl(text, lenient = true) {
|
|
304
305
|
if (typeof text !== "string") throw new TypeError("Expected a string");
|
|
305
306
|
text = text.trim();
|
|
306
307
|
if (text.includes(" ")) return false;
|
|
307
308
|
if (URL.canParse(text)) return true;
|
|
308
|
-
|
|
309
|
-
return false;
|
|
309
|
+
return lenient && URL.canParse(`https://${text}`);
|
|
310
310
|
}
|
|
311
311
|
//#endregion
|
|
312
312
|
//#region src/lib/readme/rules/code.ts
|
|
313
|
-
const LEADING_DOT_REGEX =
|
|
313
|
+
const LEADING_DOT_REGEX = /^\./v;
|
|
314
314
|
var code_default = { code: { async content(options) {
|
|
315
315
|
const validOptions = z.object({
|
|
316
316
|
file: z.string(),
|
|
@@ -387,7 +387,7 @@ var dependencies_default = { dependencies: { async content() {
|
|
|
387
387
|
if (engines?.[platformKey] !== void 0) continue;
|
|
388
388
|
const info = PLATFORM_INFO[platformKey.toLowerCase()];
|
|
389
389
|
const display = info ? `[${info.display}](${info.url})` : platformKey;
|
|
390
|
-
platformItems.push(version ? `- ${display}
|
|
390
|
+
platformItems.push(version === void 0 || version === "" ? `- ${display}` : `- ${display} ${version}`);
|
|
391
391
|
}
|
|
392
392
|
if (operatingSystem !== void 0 && operatingSystem.length > 0) platformItems.push(`- Supported platforms: ${operatingSystem.join(", ")}`);
|
|
393
393
|
const peerItems = [];
|
|
@@ -449,7 +449,7 @@ var title_default = { title: {
|
|
|
449
449
|
},
|
|
450
450
|
order: 2
|
|
451
451
|
} };
|
|
452
|
-
const SPLIT_FOR_TITLE_CASE_REGEX = /[ _
|
|
452
|
+
const SPLIT_FOR_TITLE_CASE_REGEX = /[ _\-]+/v;
|
|
453
453
|
function makeTitleCase(text) {
|
|
454
454
|
return text.split(SPLIT_FOR_TITLE_CASE_REGEX).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
455
455
|
}
|
|
@@ -520,7 +520,7 @@ async function createSizeReport(filePath) {
|
|
|
520
520
|
original: createSizeInfo(originalSize, originalSize)
|
|
521
521
|
};
|
|
522
522
|
} catch (error) {
|
|
523
|
-
throw new Error(`Failed to analyze file: ${error instanceof Error ? error.message : "Unknown error"}
|
|
523
|
+
throw new Error(`Failed to analyze file: ${error instanceof Error ? error.message : "Unknown error"}`, { cause: error });
|
|
524
524
|
}
|
|
525
525
|
}
|
|
526
526
|
//#endregion
|
|
@@ -615,8 +615,8 @@ var table_of_contents_default = { "table-of-contents": {
|
|
|
615
615
|
]).optional() }).optional().parse(options)?.depth ?? 3,
|
|
616
616
|
tight: true
|
|
617
617
|
});
|
|
618
|
-
const heading = `## Table of contents`;
|
|
619
618
|
if (result.map === void 0) throw new Error("Could not generate table of contents");
|
|
619
|
+
const heading = `## Table of contents`;
|
|
620
620
|
const rootWrapper = {
|
|
621
621
|
children: result.map.children,
|
|
622
622
|
type: "root"
|
|
@@ -674,13 +674,13 @@ function getAdditionalConfigExplorer() {
|
|
|
674
674
|
async function loadConfig(options) {
|
|
675
675
|
const { additionalConfig, defaults = rules_default, searchFrom } = options ?? {};
|
|
676
676
|
let finalConfig = { mdat: `Powered by the Markdown Autophagic Template system: [mdat](https://github.com/kitschpatrol/mdat).` };
|
|
677
|
-
|
|
677
|
+
finalConfig = deepMergeDefined(finalConfig, defaults);
|
|
678
678
|
const results = await getConfigExplorer().search(searchFrom);
|
|
679
679
|
if (results) {
|
|
680
680
|
const { config, filepath } = results;
|
|
681
681
|
let possibleRules = config;
|
|
682
682
|
log.debug(`Using config from "${filepath}"`);
|
|
683
|
-
if (filepath.endsWith("package.json")
|
|
683
|
+
if (typeof config === "string" && filepath.endsWith("package.json")) {
|
|
684
684
|
log.debug(`Detected shared config string: "${config}"`);
|
|
685
685
|
const { default: sharedConfig } = await import(config);
|
|
686
686
|
possibleRules = sharedConfig;
|
|
@@ -693,14 +693,14 @@ async function loadConfig(options) {
|
|
|
693
693
|
for (const configOrPath of additionalConfigArray) {
|
|
694
694
|
let loaded;
|
|
695
695
|
if (typeof configOrPath === "string") {
|
|
696
|
-
let
|
|
697
|
-
if (path.basename(configOrPath).endsWith("package.json"))
|
|
696
|
+
let loadResults;
|
|
697
|
+
if (path.basename(configOrPath).endsWith("package.json")) loadResults = {
|
|
698
698
|
config: mdatJsonLoader(configOrPath, await fs.readFile(configOrPath, "utf8")),
|
|
699
699
|
filepath: configOrPath
|
|
700
700
|
};
|
|
701
|
-
else
|
|
702
|
-
if (
|
|
703
|
-
const { config: loadedConfig, filepath } =
|
|
701
|
+
else loadResults = await getAdditionalConfigExplorer().load(configOrPath);
|
|
702
|
+
if (loadResults === null || loadResults === void 0) continue;
|
|
703
|
+
const { config: loadedConfig, filepath } = loadResults;
|
|
704
704
|
log.debug(`Loaded additional config from "${filepath}"`);
|
|
705
705
|
loaded = loadedConfig;
|
|
706
706
|
} else loaded = configOrPath;
|
|
@@ -745,7 +745,7 @@ async function formatWithPrettier(content, filePath) {
|
|
|
745
745
|
} catch {
|
|
746
746
|
throw new Error("The --format flag requires `prettier` to be installed. Run: pnpm add -D prettier");
|
|
747
747
|
}
|
|
748
|
-
const configKey = filePath ?
|
|
748
|
+
const configKey = filePath === void 0 || filePath === "" ? process.cwd() : path.dirname(filePath);
|
|
749
749
|
let config = configCache.get(configKey);
|
|
750
750
|
if (config === void 0 && !configCache.has(configKey)) {
|
|
751
751
|
config = await cachedPrettier.resolveConfig(filePath ?? process.cwd());
|
|
@@ -767,22 +767,22 @@ function zeroPad(n, nMax) {
|
|
|
767
767
|
async function getInputOutputPaths(inputs, output, name, extension) {
|
|
768
768
|
const paths = [];
|
|
769
769
|
for (const [index, file] of inputs.entries()) {
|
|
770
|
-
const inputOutputPath = await getInputOutputPath(file, output, name, extension, name && inputs.length > 1 ? `-${zeroPad(index + 1, inputs.length)}` : "");
|
|
770
|
+
const inputOutputPath = await getInputOutputPath(file, output, name, extension, name !== void 0 && name !== "" && inputs.length > 1 ? `-${zeroPad(index + 1, inputs.length)}` : "");
|
|
771
771
|
paths.push(inputOutputPath);
|
|
772
772
|
}
|
|
773
773
|
return paths;
|
|
774
774
|
}
|
|
775
775
|
async function getInputOutputPath(input, output, name, extension, nameSuffix = "") {
|
|
776
776
|
const resolvedInput = expandPath(input);
|
|
777
|
-
const resolvedOutput = output ? expandPath(output) : void 0;
|
|
778
777
|
if (!isFileSync(resolvedInput)) throw new Error(`Input file not found: "${resolvedInput}"`);
|
|
779
|
-
|
|
778
|
+
const resolvedOutput = output === void 0 || output === "" ? void 0 : expandPath(output);
|
|
779
|
+
if (resolvedOutput !== void 0) {
|
|
780
780
|
if (isFileSync(resolvedOutput)) throw new Error(`Output path must be a directory, received a file path: "${resolvedOutput}"`);
|
|
781
781
|
await fs.mkdir(resolvedOutput, { recursive: true });
|
|
782
782
|
}
|
|
783
783
|
return {
|
|
784
784
|
input: resolvedInput,
|
|
785
|
-
name: `${name ? path.basename(
|
|
785
|
+
name: `${name === void 0 || name === "" ? path.basename(resolvedInput, path.extname(resolvedInput)) : path.basename(name, path.extname(name))}${nameSuffix}${extension === void 0 ? name !== void 0 && path.extname(name) !== "" ? path.extname(name) : path.extname(input) : `.${extension}`}`,
|
|
786
786
|
output: resolvedOutput ?? path.dirname(resolvedInput)
|
|
787
787
|
};
|
|
788
788
|
}
|
|
@@ -793,7 +793,7 @@ function ensureArray(value) {
|
|
|
793
793
|
if (value === void 0 || value === null) return [];
|
|
794
794
|
return Array.isArray(value) ? value : [value];
|
|
795
795
|
}
|
|
796
|
-
const README_SEARCH_REGEX = /^readme(?:\.\w+)?$/
|
|
796
|
+
const README_SEARCH_REGEX = /^readme(?:\.\w+)?$/iv;
|
|
797
797
|
/**
|
|
798
798
|
* Finds a readme file in the current working directory (case-insensitive).
|
|
799
799
|
*/
|
|
@@ -869,20 +869,20 @@ function stripLintPlugins(config) {
|
|
|
869
869
|
//#endregion
|
|
870
870
|
//#region src/lib/processors.ts
|
|
871
871
|
async function processFiles(files, loader, processorGetter, name, output, config) {
|
|
872
|
-
const [resolvedConfig,
|
|
872
|
+
const [resolvedConfig, localRemarkConfig] = await Promise.all([loader({ additionalConfig: config }), loadAmbientRemarkConfig()]);
|
|
873
873
|
const inputOutputPaths = await getInputOutputPaths(ensureArray(files), output, name, "md");
|
|
874
|
-
const resolvedProcessor = processorGetter(resolvedConfig,
|
|
875
|
-
return await Promise.all(inputOutputPaths.map(async ({ input, name, output }) => {
|
|
874
|
+
const resolvedProcessor = processorGetter(resolvedConfig, localRemarkConfig);
|
|
875
|
+
return await Promise.all(inputOutputPaths.map(async ({ input, name: outputName, output: outputDirectory }) => {
|
|
876
876
|
const inputFile = await read(input);
|
|
877
877
|
const result = await resolvedProcessor.process(inputFile);
|
|
878
|
-
result.dirname =
|
|
879
|
-
result.basename =
|
|
878
|
+
result.dirname = outputDirectory;
|
|
879
|
+
result.basename = outputName;
|
|
880
880
|
return result;
|
|
881
881
|
}));
|
|
882
882
|
}
|
|
883
883
|
async function processString(markdown, loader, processorGetter, config) {
|
|
884
|
-
const [resolvedConfig,
|
|
885
|
-
return processorGetter(resolvedConfig,
|
|
884
|
+
const [resolvedConfig, localRemarkConfig] = await Promise.all([loader({ additionalConfig: config }), loadAmbientRemarkConfig()]);
|
|
885
|
+
return processorGetter(resolvedConfig, localRemarkConfig).process(new VFile(markdown));
|
|
886
886
|
}
|
|
887
887
|
function getExpandProcessor(config, ambientRemarkConfig) {
|
|
888
888
|
return remark().use({ settings: {
|
|
@@ -951,13 +951,15 @@ async function createReadmeInteractive() {
|
|
|
951
951
|
const readmePath = await findReadme();
|
|
952
952
|
intro(`Running ${picocolors.bold("mdat create")} interactively`);
|
|
953
953
|
const newReadmePath = await createReadme(await group({
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
954
|
+
async overwrite() {
|
|
955
|
+
if (readmePath === void 0) return true;
|
|
956
|
+
if (await confirm({
|
|
957
|
+
message: `Found an existing readme at "${picocolors.blue(readmePath)}". Do you want to overwrite it?`,
|
|
958
|
+
active: "Overwrite",
|
|
959
|
+
inactive: "Exit"
|
|
960
|
+
}) === false) throw new Error("`mdat create` was cancelled to avoid an overwrite - no changes were made");
|
|
961
|
+
return true;
|
|
962
|
+
},
|
|
961
963
|
template: async () => select({
|
|
962
964
|
message: "Which template would you like to use?",
|
|
963
965
|
options: getTemplateOptions()
|
|
@@ -999,17 +1001,24 @@ async function createReadme(options) {
|
|
|
999
1001
|
const templateString = getTemplateForConfig(resolvedOptions.template, resolvedOptions.compound);
|
|
1000
1002
|
const readmePath = path.join(resolvedOptions.output, "readme.md");
|
|
1001
1003
|
if (!resolvedOptions.overwrite) {
|
|
1002
|
-
|
|
1004
|
+
let exists = true;
|
|
1005
|
+
try {
|
|
1006
|
+
await fs.access(readmePath);
|
|
1007
|
+
} catch {
|
|
1008
|
+
exists = false;
|
|
1009
|
+
}
|
|
1010
|
+
if (exists) throw new Error(`Readme already exists at "${readmePath}". Use the overwrite option to replace it.`);
|
|
1003
1011
|
}
|
|
1004
1012
|
await fs.writeFile(readmePath, templateString, "utf8");
|
|
1005
1013
|
if (resolvedOptions.expand) {
|
|
1006
1014
|
const [result] = await expand(readmePath);
|
|
1015
|
+
if (result === void 0) throw new Error(`Failed to expand readme at "${readmePath}"`);
|
|
1007
1016
|
await write(result);
|
|
1008
1017
|
}
|
|
1009
1018
|
return readmePath;
|
|
1010
1019
|
}
|
|
1011
1020
|
function getTemplateForConfig(templateKey, compound) {
|
|
1012
|
-
if (!(templateKey
|
|
1021
|
+
if (!Object.hasOwn(templates_default, templateKey)) throw new Error(`Unknown template "${templateKey}". Available templates: ${Object.keys(templates_default).join(", ")}`);
|
|
1013
1022
|
return templates_default[templateKey].content[compound ? "compound" : "explicit"];
|
|
1014
1023
|
}
|
|
1015
1024
|
function getTemplateOptions() {
|
|
@@ -1097,7 +1106,11 @@ async function check(files, config, options) {
|
|
|
1097
1106
|
const resolvedFiles = Array.isArray(files) ? files : [files];
|
|
1098
1107
|
const [originals, results] = await Promise.all([Promise.all(resolvedFiles.map(async (f) => read(f))), processFiles(files, loadConfig, getExpandProcessor, void 0, void 0, config)]);
|
|
1099
1108
|
if (options?.format) await formatResults(results);
|
|
1100
|
-
return results.map((result, i) =>
|
|
1109
|
+
return results.map((result, i) => {
|
|
1110
|
+
const original = originals[i];
|
|
1111
|
+
if (original === void 0) throw new Error(`Missing original file for comparison at index ${i}`);
|
|
1112
|
+
return compareWithDiff(original, result, options);
|
|
1113
|
+
});
|
|
1101
1114
|
}
|
|
1102
1115
|
/**
|
|
1103
1116
|
* Check if MDAT comments in a Markdown string are up to date by expanding and
|
|
@@ -1130,7 +1143,7 @@ function compareWithDiff(original, result, options) {
|
|
|
1130
1143
|
};
|
|
1131
1144
|
}
|
|
1132
1145
|
async function formatResults(results) {
|
|
1133
|
-
for (const file of results) file.value = await formatWithPrettier(file.toString(), file.path
|
|
1146
|
+
for (const file of results) file.value = await formatWithPrettier(file.toString(), file.path === "" ? void 0 : file.path);
|
|
1134
1147
|
}
|
|
1135
1148
|
//#endregion
|
|
1136
1149
|
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": "
|
|
3
|
+
"version": "3.0.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",
|
|
@@ -43,24 +43,24 @@
|
|
|
43
43
|
"dist/*"
|
|
44
44
|
],
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@clack/prompts": "^1.
|
|
47
|
-
"cosmiconfig": "^9.0.
|
|
46
|
+
"@clack/prompts": "^1.7.0",
|
|
47
|
+
"cosmiconfig": "^9.0.2",
|
|
48
48
|
"cosmiconfig-typescript-loader": "^6.3.0",
|
|
49
49
|
"deepmerge-ts": "^7.1.5",
|
|
50
|
-
"globby": "^16.2.
|
|
51
|
-
"lognow": "^0.
|
|
50
|
+
"globby": "^16.2.2",
|
|
51
|
+
"lognow": "^0.7.1",
|
|
52
52
|
"mdast-util-toc": "^7.1.0",
|
|
53
|
-
"metascope": "^0.
|
|
53
|
+
"metascope": "^0.9.0",
|
|
54
54
|
"path-type": "^6.0.0",
|
|
55
55
|
"picocolors": "^1.1.1",
|
|
56
56
|
"plur": "^6.0.0",
|
|
57
|
-
"pretty-bytes": "^7.1.
|
|
57
|
+
"pretty-bytes": "^7.1.1",
|
|
58
58
|
"pretty-ms": "^9.3.0",
|
|
59
59
|
"remark": "^15.0.1",
|
|
60
60
|
"remark-gfm": "^4.0.1",
|
|
61
|
-
"remark-mdat": "^
|
|
61
|
+
"remark-mdat": "^3.0.0",
|
|
62
62
|
"to-vfile": "^8.0.0",
|
|
63
|
-
"type-fest": "^5.
|
|
63
|
+
"type-fest": "^5.8.0",
|
|
64
64
|
"unified-engine": "^11.2.2",
|
|
65
65
|
"untildify": "^6.0.0",
|
|
66
66
|
"vfile": "^6.0.3",
|
|
@@ -68,26 +68,26 @@
|
|
|
68
68
|
"zod": "^4.4.3"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
|
-
"@arethetypeswrong/core": "^0.18.
|
|
72
|
-
"@kitschpatrol/shared-config": "^
|
|
71
|
+
"@arethetypeswrong/core": "^0.18.5",
|
|
72
|
+
"@kitschpatrol/shared-config": "^8.4.0",
|
|
73
73
|
"@types/mdast": "^4.0.4",
|
|
74
|
-
"@types/node": "~
|
|
74
|
+
"@types/node": "~24.13.3",
|
|
75
75
|
"@types/unist": "^3.0.3",
|
|
76
76
|
"@types/yargs": "^17.0.35",
|
|
77
|
-
"@vitest/coverage-v8": "^4.1.
|
|
78
|
-
"bumpp": "^
|
|
79
|
-
"execa": "^
|
|
80
|
-
"mdat-plugin-cli-help": "^3.0.
|
|
77
|
+
"@vitest/coverage-v8": "^4.1.10",
|
|
78
|
+
"bumpp": "^12.0.0",
|
|
79
|
+
"execa": "^10.0.0",
|
|
80
|
+
"mdat-plugin-cli-help": "^3.0.2",
|
|
81
81
|
"mdat-plugin-example": "^2.0.1",
|
|
82
|
-
"mdat-plugin-tldraw": "^2.0.
|
|
83
|
-
"nanoid": "^
|
|
84
|
-
"prettier": "^3.
|
|
85
|
-
"publint": "^0.3.
|
|
82
|
+
"mdat-plugin-tldraw": "^2.0.3",
|
|
83
|
+
"nanoid": "^6.0.0",
|
|
84
|
+
"prettier": "^3.9.6",
|
|
85
|
+
"publint": "^0.3.22",
|
|
86
86
|
"shx": "^0.4.0",
|
|
87
|
-
"tsdown": "^0.22.
|
|
88
|
-
"typescript": "~
|
|
87
|
+
"tsdown": "^0.22.14",
|
|
88
|
+
"typescript": "~6.0.3",
|
|
89
89
|
"unplugin-raw": "^0.7.0",
|
|
90
|
-
"vitest": "^4.1.
|
|
90
|
+
"vitest": "^4.1.10"
|
|
91
91
|
},
|
|
92
92
|
"peerDependencies": {
|
|
93
93
|
"prettier": "^3.0.0"
|
|
@@ -98,13 +98,7 @@
|
|
|
98
98
|
}
|
|
99
99
|
},
|
|
100
100
|
"engines": {
|
|
101
|
-
"node": ">=
|
|
102
|
-
},
|
|
103
|
-
"devEngines": {
|
|
104
|
-
"runtime": {
|
|
105
|
-
"name": "node",
|
|
106
|
-
"version": ">=22.22.2"
|
|
107
|
-
}
|
|
101
|
+
"node": ">=24.16.0"
|
|
108
102
|
},
|
|
109
103
|
"scripts": {
|
|
110
104
|
"bench": "vitest bench --run --compare test/benchmarks/baseline.json",
|
package/readme.md
CHANGED
|
@@ -103,7 +103,7 @@ The `<!-- title -->` comment was expanded with content derived from your project
|
|
|
103
103
|
|
|
104
104
|
### Dependencies
|
|
105
105
|
|
|
106
|
-
Node
|
|
106
|
+
Node 24+ (specifically `>=24.16.0`). Written in TypeScript with bundled type definitions.
|
|
107
107
|
|
|
108
108
|
### Installation
|
|
109
109
|
|
|
@@ -531,9 +531,7 @@ type Rule =
|
|
|
531
531
|
| string
|
|
532
532
|
| {
|
|
533
533
|
content:
|
|
534
|
-
|
|
535
|
-
| Rule[]
|
|
536
|
-
| string
|
|
534
|
+
((options: JsonValue, context: RuleContext) => Promise<string> | string) | Rule[] | string
|
|
537
535
|
order?: number
|
|
538
536
|
}
|
|
539
537
|
|