mdat 2.3.6 → 3.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 +205 -103
- package/dist/lib/index.d.ts +22 -1
- package/dist/lib/index.js +159 -66
- package/package.json +26 -32
- package/readme.md +12 -10
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,21 @@ 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.1.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
|
+
const stripped = objects.map((v, i) => i === 0 ? v : stripUndefinedDeep(v));
|
|
45
|
+
return deepmerge(...stripped);
|
|
46
|
+
}
|
|
33
47
|
|
|
34
48
|
//#endregion
|
|
35
49
|
//#region src/lib/log.ts
|
|
@@ -55,19 +69,6 @@ function setLogger$2(logger) {
|
|
|
55
69
|
setLogger$1(getChildLogger(log, "metascope"));
|
|
56
70
|
}
|
|
57
71
|
|
|
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
72
|
//#endregion
|
|
72
73
|
//#region src/lib/mdat-json-loader.ts
|
|
73
74
|
/**
|
|
@@ -82,7 +83,7 @@ function mdatJsonLoader(filePath, content) {
|
|
|
82
83
|
}
|
|
83
84
|
function flattenJson(jsonObject, parentKey = "", result = {}) {
|
|
84
85
|
for (const [key, value] of Object.entries(jsonObject)) {
|
|
85
|
-
const fullPath = parentKey ? `${parentKey}.${key}
|
|
86
|
+
const fullPath = parentKey === "" ? key : `${parentKey}.${key}`;
|
|
86
87
|
if (typeof value === "object" && value !== null && !Array.isArray(value)) flattenJson(value, fullPath, result);
|
|
87
88
|
else if (value === null) result[fullPath] = "null";
|
|
88
89
|
else result[fullPath] = value.toString();
|
|
@@ -111,6 +112,7 @@ async function getContextMetadata() {
|
|
|
111
112
|
"codemetaJson",
|
|
112
113
|
"gitConfig",
|
|
113
114
|
"githubActions",
|
|
115
|
+
"gitStats",
|
|
114
116
|
"goGoMod",
|
|
115
117
|
"goGoreleaserYaml",
|
|
116
118
|
"javaPomXml",
|
|
@@ -137,16 +139,17 @@ async function getContextMetadata() {
|
|
|
137
139
|
});
|
|
138
140
|
return metascopeMetadata;
|
|
139
141
|
}
|
|
140
|
-
const GIT_PREFIX_REGEX = /^git
|
|
141
|
-
const GIT_SUFFIX_REGEX = /\.git
|
|
142
|
-
const TRAILING_SLASH_REGEX =
|
|
142
|
+
const GIT_PREFIX_REGEX = /^git\+/v;
|
|
143
|
+
const GIT_SUFFIX_REGEX = /\.git$/v;
|
|
144
|
+
const TRAILING_SLASH_REGEX = /\/$/v;
|
|
145
|
+
const PACKAGE_MANAGER_REGEX = /^(?<name>[^@]+)@(?<version>[^+]+)/v;
|
|
143
146
|
const readmeMetadataTemplate = defineTemplate((context) => {
|
|
144
|
-
const { githubActions, licenseFile, metascope, nodePackageJson } = context;
|
|
147
|
+
const { githubActions, gitStats, licenseFile, metascope, nodePackageJson } = context;
|
|
145
148
|
const codemeta = templates.codemetaJson(context, {});
|
|
146
149
|
const nodePackage = helpers.firstOf(nodePackageJson)?.data;
|
|
147
150
|
const licenseFileData = helpers.firstOf(licenseFile);
|
|
148
151
|
const ciActionFilePath = helpers.ensureArray(githubActions).find((entry) => entry.data.name.toLowerCase() === "ci")?.source;
|
|
149
|
-
const
|
|
152
|
+
const repoUrl = codemeta.codeRepository?.replace(GIT_PREFIX_REGEX, "").replace(GIT_SUFFIX_REGEX, "").replace(TRAILING_SLASH_REGEX, "");
|
|
150
153
|
const bin = (() => {
|
|
151
154
|
if (nodePackage === void 0) return;
|
|
152
155
|
const binField = nodePackage.bin;
|
|
@@ -174,13 +177,35 @@ const readmeMetadataTemplate = defineTemplate((context) => {
|
|
|
174
177
|
version
|
|
175
178
|
}));
|
|
176
179
|
})();
|
|
180
|
+
const developmentDependencies = (() => {
|
|
181
|
+
if (nodePackage === void 0) return;
|
|
182
|
+
const runtimes = helpers.ensureArray(nodePackage.devEngines?.runtime).map(({ name, version }) => ({
|
|
183
|
+
name,
|
|
184
|
+
version
|
|
185
|
+
}));
|
|
186
|
+
const packageManagers = helpers.ensureArray(nodePackage.devEngines?.packageManager).map(({ name, version }) => ({
|
|
187
|
+
name,
|
|
188
|
+
version
|
|
189
|
+
}));
|
|
190
|
+
const pinnedPackageManager = nodePackage.packageManager === void 0 ? void 0 : PACKAGE_MANAGER_REGEX.exec(nodePackage.packageManager)?.groups;
|
|
191
|
+
if (pinnedPackageManager?.name !== void 0 && pinnedPackageManager.version !== void 0 && packageManagers.every((entry) => entry.name !== pinnedPackageManager.name)) packageManagers.push({
|
|
192
|
+
name: pinnedPackageManager.name,
|
|
193
|
+
version: pinnedPackageManager.version
|
|
194
|
+
});
|
|
195
|
+
if (runtimes.length === 0 && packageManagers.length === 0) return;
|
|
196
|
+
return {
|
|
197
|
+
packageManagers: packageManagers.length > 0 ? packageManagers : void 0,
|
|
198
|
+
runtimes: runtimes.length > 0 ? runtimes : void 0
|
|
199
|
+
};
|
|
200
|
+
})();
|
|
177
201
|
const firstAuthor = helpers.firstOf(helpers.ensureArray(codemeta.author));
|
|
178
202
|
return {
|
|
179
203
|
author: helpers.firstOf(helpers.mixedStringsToArray(helpers.toBasicNames(codemeta.author))),
|
|
180
204
|
authorUrl: firstAuthor?.url,
|
|
181
205
|
bin,
|
|
182
|
-
ciActionFileName: ciActionFilePath ? path.basename(ciActionFilePath)
|
|
206
|
+
ciActionFileName: ciActionFilePath === void 0 ? void 0 : path.basename(ciActionFilePath),
|
|
183
207
|
description: codemeta.description,
|
|
208
|
+
developmentDependencies,
|
|
184
209
|
engines,
|
|
185
210
|
isPublicNpmPackage: nodePackage?.private !== true && (nodePackage?.name.startsWith("@") && nodePackage.publishConfig?.access === "public" || true),
|
|
186
211
|
issuesUrl: codemeta.issueTracker,
|
|
@@ -191,8 +216,9 @@ const readmeMetadataTemplate = defineTemplate((context) => {
|
|
|
191
216
|
operatingSystem: codemeta.operatingSystem,
|
|
192
217
|
peerDependencies,
|
|
193
218
|
projectDirectory: metascope?.data.options.path === void 0 ? void 0 : `file://${metascope.data.options.path}`,
|
|
194
|
-
repositoryUrl,
|
|
219
|
+
repositoryUrl: repoUrl,
|
|
195
220
|
runtimePlatform: codemeta.runtimePlatform,
|
|
221
|
+
usesGitLfs: helpers.firstOf(gitStats)?.data.hasLfs === true,
|
|
196
222
|
usesPnpm: helpers.usesPnpm(nodePackageJson)
|
|
197
223
|
};
|
|
198
224
|
});
|
|
@@ -204,7 +230,8 @@ let readmeMetadata;
|
|
|
204
230
|
*/
|
|
205
231
|
async function getReadmeMetadata() {
|
|
206
232
|
if (readmeMetadata !== void 0) return readmeMetadata;
|
|
207
|
-
|
|
233
|
+
const contextMetadata = await getContextMetadata();
|
|
234
|
+
readmeMetadata = readmeMetadataTemplate(contextMetadata, {});
|
|
208
235
|
return readmeMetadata;
|
|
209
236
|
}
|
|
210
237
|
|
|
@@ -219,14 +246,15 @@ var badges_default = { badges: { async content(options) {
|
|
|
219
246
|
npm: z.array(z.string()).optional()
|
|
220
247
|
}).optional().parse(options);
|
|
221
248
|
const metadata = await getReadmeMetadata();
|
|
222
|
-
const { ciActionFileName, license, licenseUrl, name, repositoryUrl } = metadata;
|
|
249
|
+
const { ciActionFileName, license, licenseUrl, name, repositoryUrl, usesGitLfs } = metadata;
|
|
223
250
|
const badges = [];
|
|
224
251
|
if (validOptions?.npm === void 0) {
|
|
225
|
-
if (metadata.isPublicNpmPackage) badges.push(`[](https://npmjs.com/package/${name})`);
|
|
226
|
-
} else for (const
|
|
252
|
+
if (metadata.isPublicNpmPackage) badges.push(`[](https://www.npmjs.com/package/${name})`);
|
|
253
|
+
} else for (const packageName of validOptions.npm) badges.push(`[](https://www.npmjs.com/package/${packageName})`);
|
|
227
254
|
if (license !== void 0 && licenseUrl !== void 0) badges.push(`[}-yellow.svg)](${licenseUrl})`);
|
|
228
255
|
if (ciActionFileName !== void 0 && repositoryUrl !== void 0) badges.push(`[](${repositoryUrl}/actions/workflows/${ciActionFileName})`);
|
|
229
|
-
if (
|
|
256
|
+
if (usesGitLfs) badges.push("");
|
|
257
|
+
if (validOptions?.custom !== void 0) for (const [badgeName, { image, link }] of Object.entries(validOptions.custom)) badges.push(`[](${link})`);
|
|
230
258
|
return badges.join("\n");
|
|
231
259
|
} } };
|
|
232
260
|
|
|
@@ -239,7 +267,7 @@ var banner_default = { banner: { async content(options) {
|
|
|
239
267
|
}).optional().parse(options);
|
|
240
268
|
const src = validOptions?.src ?? await getBannerSrc();
|
|
241
269
|
if (src === void 0) throw new Error("Banner image not found at any typical location, consider adding something at ./assets/banner.webp");
|
|
242
|
-
|
|
270
|
+
if (!isUrl(src) && !await isFile(src)) throw new Error(`Banner image not found at "${src}"`);
|
|
243
271
|
let alt = validOptions?.alt;
|
|
244
272
|
if (alt === void 0) {
|
|
245
273
|
const { name: packageName } = await getReadmeMetadata();
|
|
@@ -251,7 +279,7 @@ var banner_default = { banner: { async content(options) {
|
|
|
251
279
|
async function getBannerSrc() {
|
|
252
280
|
const { projectDirectory } = await getReadmeMetadata();
|
|
253
281
|
if (projectDirectory === void 0) throw new Error("No project directory found");
|
|
254
|
-
const
|
|
282
|
+
const [firstPath] = await globby([
|
|
255
283
|
".",
|
|
256
284
|
"assets",
|
|
257
285
|
"media",
|
|
@@ -287,20 +315,19 @@ async function getBannerSrc() {
|
|
|
287
315
|
]
|
|
288
316
|
}
|
|
289
317
|
});
|
|
290
|
-
|
|
318
|
+
return firstPath === void 0 ? void 0 : path.relative(process.cwd(), firstPath);
|
|
291
319
|
}
|
|
292
320
|
function isUrl(text, lenient = true) {
|
|
293
321
|
if (typeof text !== "string") throw new TypeError("Expected a string");
|
|
294
322
|
text = text.trim();
|
|
295
323
|
if (text.includes(" ")) return false;
|
|
296
324
|
if (URL.canParse(text)) return true;
|
|
297
|
-
|
|
298
|
-
return false;
|
|
325
|
+
return lenient && URL.canParse(`https://${text}`);
|
|
299
326
|
}
|
|
300
327
|
|
|
301
328
|
//#endregion
|
|
302
329
|
//#region src/lib/readme/rules/code.ts
|
|
303
|
-
const LEADING_DOT_REGEX =
|
|
330
|
+
const LEADING_DOT_REGEX = /^\./v;
|
|
304
331
|
var code_default = { code: { async content(options) {
|
|
305
332
|
const validOptions = z.object({
|
|
306
333
|
file: z.string(),
|
|
@@ -349,7 +376,7 @@ const PLATFORM_INFO = {
|
|
|
349
376
|
},
|
|
350
377
|
node: {
|
|
351
378
|
display: "Node.js",
|
|
352
|
-
url: "https://nodejs.org/"
|
|
379
|
+
url: "https://nodejs.org/en"
|
|
353
380
|
},
|
|
354
381
|
python: {
|
|
355
382
|
display: "Python",
|
|
@@ -379,7 +406,7 @@ var dependencies_default = { dependencies: { async content() {
|
|
|
379
406
|
if (engines?.[platformKey] !== void 0) continue;
|
|
380
407
|
const info = PLATFORM_INFO[platformKey.toLowerCase()];
|
|
381
408
|
const display = info ? `[${info.display}](${info.url})` : platformKey;
|
|
382
|
-
platformItems.push(version ? `- ${display}
|
|
409
|
+
platformItems.push(version === void 0 || version === "" ? `- ${display}` : `- ${display} ${version}`);
|
|
383
410
|
}
|
|
384
411
|
if (operatingSystem !== void 0 && operatingSystem.length > 0) platformItems.push(`- Supported platforms: ${operatingSystem.join(", ")}`);
|
|
385
412
|
const peerItems = [];
|
|
@@ -409,6 +436,58 @@ var description_default = { description: { async content() {
|
|
|
409
436
|
return `**${description}**`;
|
|
410
437
|
} } };
|
|
411
438
|
|
|
439
|
+
//#endregion
|
|
440
|
+
//#region src/lib/readme/rules/development-dependencies.ts
|
|
441
|
+
const TOOL_INFO = {
|
|
442
|
+
bun: {
|
|
443
|
+
display: "Bun",
|
|
444
|
+
url: "https://bun.sh/"
|
|
445
|
+
},
|
|
446
|
+
deno: {
|
|
447
|
+
display: "Deno",
|
|
448
|
+
url: "https://deno.land/"
|
|
449
|
+
},
|
|
450
|
+
node: {
|
|
451
|
+
display: "Node.js",
|
|
452
|
+
url: "https://nodejs.org/en"
|
|
453
|
+
},
|
|
454
|
+
npm: {
|
|
455
|
+
display: "npm",
|
|
456
|
+
url: "https://www.npmjs.com/"
|
|
457
|
+
},
|
|
458
|
+
pnpm: {
|
|
459
|
+
display: "pnpm",
|
|
460
|
+
url: "https://pnpm.io/"
|
|
461
|
+
},
|
|
462
|
+
yarn: {
|
|
463
|
+
display: "Yarn",
|
|
464
|
+
url: "https://yarnpkg.com/"
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
var development_dependencies_default = { "development-dependencies": { async content() {
|
|
468
|
+
const { developmentDependencies } = await getReadmeMetadata();
|
|
469
|
+
if (developmentDependencies === void 0) return "";
|
|
470
|
+
const { packageManagers, runtimes } = developmentDependencies;
|
|
471
|
+
const items = [];
|
|
472
|
+
for (const { name, version } of [...runtimes ?? [], ...packageManagers ?? []]) {
|
|
473
|
+
const info = TOOL_INFO[name.toLowerCase()];
|
|
474
|
+
const display = info ? `[${info.display}](${info.url})` : name;
|
|
475
|
+
items.push(version === void 0 || version === "" ? `- ${display}` : `- ${display} ${version}`);
|
|
476
|
+
}
|
|
477
|
+
return [
|
|
478
|
+
"## Development Dependencies",
|
|
479
|
+
"",
|
|
480
|
+
...items
|
|
481
|
+
].join("\n");
|
|
482
|
+
} } };
|
|
483
|
+
|
|
484
|
+
//#endregion
|
|
485
|
+
//#region src/lib/readme/rules/dev-dependencies.ts
|
|
486
|
+
/**
|
|
487
|
+
* Simple alias for `development-dependencies`
|
|
488
|
+
*/
|
|
489
|
+
var dev_dependencies_default = { "dev-dependencies": getSoleRule(development_dependencies_default) };
|
|
490
|
+
|
|
412
491
|
//#endregion
|
|
413
492
|
//#region src/lib/readme/rules/license.ts
|
|
414
493
|
var license_default = { license: { async content() {
|
|
@@ -446,7 +525,7 @@ var title_default = { title: {
|
|
|
446
525
|
},
|
|
447
526
|
order: 2
|
|
448
527
|
} };
|
|
449
|
-
const SPLIT_FOR_TITLE_CASE_REGEX = /[ _
|
|
528
|
+
const SPLIT_FOR_TITLE_CASE_REGEX = /[ _\-]+/v;
|
|
450
529
|
function makeTitleCase(text) {
|
|
451
530
|
return text.split(SPLIT_FOR_TITLE_CASE_REGEX).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
452
531
|
}
|
|
@@ -520,7 +599,7 @@ async function createSizeReport(filePath) {
|
|
|
520
599
|
original: createSizeInfo(originalSize, originalSize)
|
|
521
600
|
};
|
|
522
601
|
} catch (error) {
|
|
523
|
-
throw new Error(`Failed to analyze file: ${error instanceof Error ? error.message : "Unknown error"}
|
|
602
|
+
throw new Error(`Failed to analyze file: ${error instanceof Error ? error.message : "Unknown error"}`, { cause: error });
|
|
524
603
|
}
|
|
525
604
|
}
|
|
526
605
|
|
|
@@ -543,7 +622,8 @@ function getSizeForCompression(report, compression) {
|
|
|
543
622
|
}
|
|
544
623
|
var size_default = { size: { async content(options) {
|
|
545
624
|
const validOptions = optionsSchema$1.parse(options);
|
|
546
|
-
|
|
625
|
+
const fullPath = path.join(process.cwd(), validOptions.file);
|
|
626
|
+
return getSizeForCompression(await createSizeReport(fullPath), validOptions.compression);
|
|
547
627
|
} } };
|
|
548
628
|
|
|
549
629
|
//#endregion
|
|
@@ -593,7 +673,8 @@ function formatMarkdownTable(reports, options) {
|
|
|
593
673
|
var size_table_default = { "size-table": { async content(options) {
|
|
594
674
|
const validOptions = optionsSchema.parse(options);
|
|
595
675
|
return formatMarkdownTable(await Promise.all(validOptions.files.map(async (file) => {
|
|
596
|
-
|
|
676
|
+
const fullPath = path.join(process.cwd(), file);
|
|
677
|
+
return [file, await createSizeReport(fullPath)];
|
|
597
678
|
})), {
|
|
598
679
|
brotli: validOptions.brotli,
|
|
599
680
|
gzip: validOptions.gzip,
|
|
@@ -606,20 +687,21 @@ var size_table_default = { "size-table": { async content(options) {
|
|
|
606
687
|
//#region src/lib/readme/rules/table-of-contents.ts
|
|
607
688
|
var table_of_contents_default = { "table-of-contents": {
|
|
608
689
|
async content(options, { tree }) {
|
|
690
|
+
const validOptions = z.object({ depth: z.union([
|
|
691
|
+
z.literal(1),
|
|
692
|
+
z.literal(2),
|
|
693
|
+
z.literal(3),
|
|
694
|
+
z.literal(4),
|
|
695
|
+
z.literal(5),
|
|
696
|
+
z.literal(6)
|
|
697
|
+
]).optional() }).optional().parse(options);
|
|
609
698
|
const result = toc(tree, {
|
|
610
699
|
heading: null,
|
|
611
|
-
maxDepth:
|
|
612
|
-
z.literal(1),
|
|
613
|
-
z.literal(2),
|
|
614
|
-
z.literal(3),
|
|
615
|
-
z.literal(4),
|
|
616
|
-
z.literal(5),
|
|
617
|
-
z.literal(6)
|
|
618
|
-
]).optional() }).optional().parse(options)?.depth ?? 3,
|
|
700
|
+
maxDepth: validOptions?.depth ?? 3,
|
|
619
701
|
tight: true
|
|
620
702
|
});
|
|
621
|
-
const heading = `## Table of contents`;
|
|
622
703
|
if (result.map === void 0) throw new Error("Could not generate table of contents");
|
|
704
|
+
const heading = `## Table of contents`;
|
|
623
705
|
const rootWrapper = {
|
|
624
706
|
children: result.map.children,
|
|
625
707
|
type: "root"
|
|
@@ -645,6 +727,8 @@ var rules_default = {
|
|
|
645
727
|
...contributing_default,
|
|
646
728
|
...dependencies_default,
|
|
647
729
|
...description_default,
|
|
730
|
+
...dev_dependencies_default,
|
|
731
|
+
...development_dependencies_default,
|
|
648
732
|
...footer_default,
|
|
649
733
|
...header_default,
|
|
650
734
|
...install_default,
|
|
@@ -680,13 +764,13 @@ function getAdditionalConfigExplorer() {
|
|
|
680
764
|
async function loadConfig(options) {
|
|
681
765
|
const { additionalConfig, defaults = rules_default, searchFrom } = options ?? {};
|
|
682
766
|
let finalConfig = { mdat: `Powered by the Markdown Autophagic Template system: [mdat](https://github.com/kitschpatrol/mdat).` };
|
|
683
|
-
|
|
767
|
+
finalConfig = deepMergeDefined(finalConfig, defaults);
|
|
684
768
|
const results = await getConfigExplorer().search(searchFrom);
|
|
685
769
|
if (results) {
|
|
686
770
|
const { config, filepath } = results;
|
|
687
771
|
let possibleRules = config;
|
|
688
772
|
log.debug(`Using config from "${filepath}"`);
|
|
689
|
-
if (filepath.endsWith("package.json")
|
|
773
|
+
if (typeof config === "string" && filepath.endsWith("package.json")) {
|
|
690
774
|
log.debug(`Detected shared config string: "${config}"`);
|
|
691
775
|
const { default: sharedConfig } = await import(config);
|
|
692
776
|
possibleRules = sharedConfig;
|
|
@@ -699,14 +783,16 @@ async function loadConfig(options) {
|
|
|
699
783
|
for (const configOrPath of additionalConfigArray) {
|
|
700
784
|
let loaded;
|
|
701
785
|
if (typeof configOrPath === "string") {
|
|
702
|
-
let
|
|
703
|
-
if (path.basename(configOrPath).endsWith("package.json"))
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
786
|
+
let loadResults;
|
|
787
|
+
if (path.basename(configOrPath).endsWith("package.json")) {
|
|
788
|
+
const packageJson = await fs.readFile(configOrPath, "utf8");
|
|
789
|
+
loadResults = {
|
|
790
|
+
config: mdatJsonLoader(configOrPath, packageJson),
|
|
791
|
+
filepath: configOrPath
|
|
792
|
+
};
|
|
793
|
+
} else loadResults = await getAdditionalConfigExplorer().load(configOrPath);
|
|
794
|
+
if (loadResults === null || loadResults === void 0) continue;
|
|
795
|
+
const { config: loadedConfig, filepath } = loadResults;
|
|
710
796
|
log.debug(`Loaded additional config from "${filepath}"`);
|
|
711
797
|
loaded = loadedConfig;
|
|
712
798
|
} else loaded = configOrPath;
|
|
@@ -740,7 +826,7 @@ async function formatWithPrettier(content, filePath) {
|
|
|
740
826
|
} catch {
|
|
741
827
|
throw new Error("The --format flag requires `prettier` to be installed. Run: pnpm add -D prettier");
|
|
742
828
|
}
|
|
743
|
-
const configKey = filePath ?
|
|
829
|
+
const configKey = filePath === void 0 || filePath === "" ? process.cwd() : path.dirname(filePath);
|
|
744
830
|
let config = configCache.get(configKey);
|
|
745
831
|
if (config === void 0 && !configCache.has(configKey)) {
|
|
746
832
|
config = await cachedPrettier.resolveConfig(filePath ?? process.cwd());
|
|
@@ -763,22 +849,22 @@ function zeroPad(n, nMax) {
|
|
|
763
849
|
async function getInputOutputPaths(inputs, output, name, extension) {
|
|
764
850
|
const paths = [];
|
|
765
851
|
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)}` : "");
|
|
852
|
+
const inputOutputPath = await getInputOutputPath(file, output, name, extension, name !== void 0 && name !== "" && inputs.length > 1 ? `-${zeroPad(index + 1, inputs.length)}` : "");
|
|
767
853
|
paths.push(inputOutputPath);
|
|
768
854
|
}
|
|
769
855
|
return paths;
|
|
770
856
|
}
|
|
771
857
|
async function getInputOutputPath(input, output, name, extension, nameSuffix = "") {
|
|
772
858
|
const resolvedInput = expandPath(input);
|
|
773
|
-
const resolvedOutput = output ? expandPath(output) : void 0;
|
|
774
859
|
if (!isFileSync(resolvedInput)) throw new Error(`Input file not found: "${resolvedInput}"`);
|
|
775
|
-
|
|
860
|
+
const resolvedOutput = output === void 0 || output === "" ? void 0 : expandPath(output);
|
|
861
|
+
if (resolvedOutput !== void 0) {
|
|
776
862
|
if (isFileSync(resolvedOutput)) throw new Error(`Output path must be a directory, received a file path: "${resolvedOutput}"`);
|
|
777
863
|
await fs.mkdir(resolvedOutput, { recursive: true });
|
|
778
864
|
}
|
|
779
865
|
return {
|
|
780
866
|
input: resolvedInput,
|
|
781
|
-
name: `${name ? path.basename(
|
|
867
|
+
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
868
|
output: resolvedOutput ?? path.dirname(resolvedInput)
|
|
783
869
|
};
|
|
784
870
|
}
|
|
@@ -789,7 +875,7 @@ function ensureArray(value) {
|
|
|
789
875
|
if (value === void 0 || value === null) return [];
|
|
790
876
|
return Array.isArray(value) ? value : [value];
|
|
791
877
|
}
|
|
792
|
-
const README_SEARCH_REGEX = /^readme(?:\.\w+)?$/
|
|
878
|
+
const README_SEARCH_REGEX = /^readme(?:\.\w+)?$/iv;
|
|
793
879
|
/**
|
|
794
880
|
* Finds a readme file in the current working directory (case-insensitive).
|
|
795
881
|
*/
|
|
@@ -866,14 +952,15 @@ function stripLintPlugins(config) {
|
|
|
866
952
|
//#endregion
|
|
867
953
|
//#region src/lib/processors.ts
|
|
868
954
|
async function processFiles(files, loader, processorGetter, name, output, config) {
|
|
869
|
-
const [resolvedConfig,
|
|
870
|
-
const
|
|
871
|
-
const
|
|
872
|
-
|
|
955
|
+
const [resolvedConfig, localRemarkConfig] = await Promise.all([loader({ additionalConfig: config }), loadAmbientRemarkConfig()]);
|
|
956
|
+
const resolvedFiles = ensureArray(files);
|
|
957
|
+
const inputOutputPaths = await getInputOutputPaths(resolvedFiles, output, name, "md");
|
|
958
|
+
const resolvedProcessor = processorGetter(resolvedConfig, localRemarkConfig);
|
|
959
|
+
return await Promise.all(inputOutputPaths.map(async ({ input, name: outputName, output: outputDirectory }) => {
|
|
873
960
|
const inputFile = await read(input);
|
|
874
961
|
const result = await resolvedProcessor.process(inputFile);
|
|
875
|
-
result.dirname =
|
|
876
|
-
result.basename =
|
|
962
|
+
result.dirname = outputDirectory;
|
|
963
|
+
result.basename = outputName;
|
|
877
964
|
return result;
|
|
878
965
|
}));
|
|
879
966
|
}
|
|
@@ -970,13 +1057,15 @@ async function createReadmeInteractive() {
|
|
|
970
1057
|
const readmePath = await findReadme();
|
|
971
1058
|
intro(`Running ${picocolors.bold("mdat create")} interactively`);
|
|
972
1059
|
const newReadmePath = await createReadme(await group({
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1060
|
+
async overwrite() {
|
|
1061
|
+
if (readmePath === void 0) return true;
|
|
1062
|
+
if (await confirm({
|
|
1063
|
+
message: `Found an existing readme at "${picocolors.blue(readmePath)}". Do you want to overwrite it?`,
|
|
1064
|
+
active: "Overwrite",
|
|
1065
|
+
inactive: "Exit"
|
|
1066
|
+
}) === false) throw new Error("`mdat create` was cancelled to avoid an overwrite - no changes were made");
|
|
1067
|
+
return true;
|
|
1068
|
+
},
|
|
980
1069
|
template: async () => select({
|
|
981
1070
|
message: "Which template would you like to use?",
|
|
982
1071
|
options: getTemplateOptions()
|
|
@@ -1018,17 +1107,24 @@ async function createReadme(options) {
|
|
|
1018
1107
|
const templateString = getTemplateForConfig(resolvedOptions.template, resolvedOptions.compound);
|
|
1019
1108
|
const readmePath = path.join(resolvedOptions.output, "readme.md");
|
|
1020
1109
|
if (!resolvedOptions.overwrite) {
|
|
1021
|
-
|
|
1110
|
+
let exists = true;
|
|
1111
|
+
try {
|
|
1112
|
+
await fs.access(readmePath);
|
|
1113
|
+
} catch {
|
|
1114
|
+
exists = false;
|
|
1115
|
+
}
|
|
1116
|
+
if (exists) throw new Error(`Readme already exists at "${readmePath}". Use the overwrite option to replace it.`);
|
|
1022
1117
|
}
|
|
1023
1118
|
await fs.writeFile(readmePath, templateString, "utf8");
|
|
1024
1119
|
if (resolvedOptions.expand) {
|
|
1025
1120
|
const [result] = await expand(readmePath);
|
|
1121
|
+
if (result === void 0) throw new Error(`Failed to expand readme at "${readmePath}"`);
|
|
1026
1122
|
await write(result);
|
|
1027
1123
|
}
|
|
1028
1124
|
return readmePath;
|
|
1029
1125
|
}
|
|
1030
1126
|
function getTemplateForConfig(templateKey, compound) {
|
|
1031
|
-
if (!(templateKey
|
|
1127
|
+
if (!Object.hasOwn(templates_default, templateKey)) throw new Error(`Unknown template "${templateKey}". Available templates: ${Object.keys(templates_default).join(", ")}`);
|
|
1032
1128
|
return templates_default[templateKey].content[compound ? "compound" : "explicit"];
|
|
1033
1129
|
}
|
|
1034
1130
|
function getTemplateOptions() {
|
|
@@ -1093,7 +1189,11 @@ async function check(files, config, options) {
|
|
|
1093
1189
|
const resolvedFiles = Array.isArray(files) ? files : [files];
|
|
1094
1190
|
const [originals, results] = await Promise.all([Promise.all(resolvedFiles.map(async (f) => read(f))), processFiles(files, loadConfig, getExpandProcessor, void 0, void 0, config)]);
|
|
1095
1191
|
if (options?.format) await formatResults(results);
|
|
1096
|
-
return results.map((result, i) =>
|
|
1192
|
+
return results.map((result, i) => {
|
|
1193
|
+
const original = originals[i];
|
|
1194
|
+
if (original === void 0) throw new Error(`Missing original file for comparison at index ${i}`);
|
|
1195
|
+
return compareWithDiff(original, result, options);
|
|
1196
|
+
});
|
|
1097
1197
|
}
|
|
1098
1198
|
function compareWithDiff(original, result, options) {
|
|
1099
1199
|
const inSync = original.toString().replaceAll("\r\n", "\n") === result.toString();
|
|
@@ -1115,7 +1215,7 @@ function compareWithDiff(original, result, options) {
|
|
|
1115
1215
|
};
|
|
1116
1216
|
}
|
|
1117
1217
|
async function formatResults(results) {
|
|
1118
|
-
for (const file of results) file.value = await formatWithPrettier(file.toString(), file.path
|
|
1218
|
+
for (const file of results) file.value = await formatWithPrettier(file.toString(), file.path === "" ? void 0 : file.path);
|
|
1119
1219
|
}
|
|
1120
1220
|
|
|
1121
1221
|
//#endregion
|
|
@@ -1195,55 +1295,57 @@ const yargsInstance = yargs(hideBin(process.argv));
|
|
|
1195
1295
|
try {
|
|
1196
1296
|
await yargsInstance.scriptName("mdat").usage("$0 [command] [files..] [options]", "Work with MDAT placeholder comments in Markdown files.").option(verboseOption).middleware((argv) => {
|
|
1197
1297
|
setLogger$2(createLogger({
|
|
1198
|
-
name,
|
|
1199
|
-
verbose: argv.verbose ?? false,
|
|
1200
1298
|
logToConsole: {
|
|
1201
1299
|
showLevel: false,
|
|
1202
1300
|
showName: false,
|
|
1203
1301
|
showTime: false
|
|
1204
|
-
}
|
|
1302
|
+
},
|
|
1303
|
+
name,
|
|
1304
|
+
verbose: argv.verbose ?? false
|
|
1205
1305
|
}));
|
|
1206
|
-
}).command(["$0 [files..] [options]", "expand [files..] [options]"], "Expand MDAT placeholder comments. If no files are provided, the closest readme.md is expanded.", (
|
|
1306
|
+
}).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
1307
|
logConflicts({
|
|
1208
|
-
name,
|
|
1308
|
+
name: fileName,
|
|
1209
1309
|
output,
|
|
1210
1310
|
print
|
|
1211
1311
|
});
|
|
1212
|
-
const
|
|
1312
|
+
const mergedConfig = collectConfig(config);
|
|
1313
|
+
const results = await expand(files, fileName, output, mergedConfig, { format });
|
|
1213
1314
|
for (const file of results) if (print) process.stdout.write(file.toString());
|
|
1214
1315
|
else await write(file);
|
|
1215
1316
|
reporterMdat(results);
|
|
1216
1317
|
log.debug(`Expanded comments in ${prettyMilliseconds(performance.now() - startTime)}.`);
|
|
1217
1318
|
process.exitCode = getExitCode(results);
|
|
1218
|
-
}).command("collapse [files..] [options]", "Collapse MDAT placeholder comments. If no files are provided, the closest readme.md is collapsed.", (
|
|
1319
|
+
}).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
1320
|
logConflicts({
|
|
1220
|
-
name,
|
|
1321
|
+
name: fileName,
|
|
1221
1322
|
output,
|
|
1222
1323
|
print
|
|
1223
1324
|
});
|
|
1224
|
-
const results = await collapse(files,
|
|
1325
|
+
const results = await collapse(files, fileName, output, void 0, { format });
|
|
1225
1326
|
for (const file of results) if (print) process.stdout.write(file.toString());
|
|
1226
1327
|
else await write(file);
|
|
1227
1328
|
reporterMdat(results);
|
|
1228
1329
|
log.debug(`Collapsed comments in ${prettyMilliseconds(performance.now() - startTime)}.`);
|
|
1229
1330
|
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.", (
|
|
1331
|
+
}).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
1332
|
logConflicts({
|
|
1232
|
-
name,
|
|
1333
|
+
name: fileName,
|
|
1233
1334
|
output,
|
|
1234
1335
|
print
|
|
1235
1336
|
});
|
|
1236
|
-
const results = await strip(files,
|
|
1337
|
+
const results = await strip(files, fileName, output, void 0, { format });
|
|
1237
1338
|
for (const file of results) if (print) process.stdout.write(file.toString());
|
|
1238
1339
|
else await write(file);
|
|
1239
1340
|
reporterMdat(results);
|
|
1240
1341
|
log.debug(`Stripped comments in ${prettyMilliseconds(performance.now() - startTime)}.`);
|
|
1241
1342
|
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.", (
|
|
1243
|
-
const
|
|
1343
|
+
}).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 }) => {
|
|
1344
|
+
const mergedConfig = collectConfig(config);
|
|
1345
|
+
const results = await check(files, mergedConfig, { format });
|
|
1244
1346
|
let allInSync = true;
|
|
1245
1347
|
for (const { inSync, result } of results) {
|
|
1246
|
-
const filePath = result.path
|
|
1348
|
+
const filePath = result.path === "" ? "unknown" : result.path;
|
|
1247
1349
|
if (inSync) log.debug(`${picocolors.green("Up to date")}: ${filePath}`);
|
|
1248
1350
|
else {
|
|
1249
1351
|
log.warn(`${picocolors.red("Stale content")}: ${filePath}`);
|
|
@@ -1253,12 +1355,12 @@ try {
|
|
|
1253
1355
|
}
|
|
1254
1356
|
log.debug(`Checked in ${prettyMilliseconds(performance.now() - startTime)}.`);
|
|
1255
1357
|
process.exitCode = allInSync ? 0 : 1;
|
|
1256
|
-
}).command("create [options]", "Create a new Markdown file from a template.", (
|
|
1358
|
+
}).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
1359
|
if (interactive) await createReadmeInteractive();
|
|
1258
1360
|
else {
|
|
1259
1361
|
const readmePath = await createReadme({
|
|
1260
1362
|
compound,
|
|
1261
|
-
expand,
|
|
1363
|
+
expand: expandTemplate,
|
|
1262
1364
|
output,
|
|
1263
1365
|
overwrite,
|
|
1264
1366
|
template
|
|
@@ -1272,11 +1374,11 @@ try {
|
|
|
1272
1374
|
process.exitCode = 1;
|
|
1273
1375
|
}
|
|
1274
1376
|
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`);
|
|
1377
|
+
if (args.print && args.output !== void 0 && args.output !== "") log.warn(`Ignoring --output option because --print is set`);
|
|
1378
|
+
if (args.print && args.name !== void 0 && args.name !== "") log.warn(`Ignoring --name option because --print is set`);
|
|
1277
1379
|
}
|
|
1278
1380
|
function collectConfig(config) {
|
|
1279
|
-
if (config === void 0) return
|
|
1381
|
+
if (config === void 0) return;
|
|
1280
1382
|
return ensureArray(config);
|
|
1281
1383
|
}
|
|
1282
1384
|
function getExitCode(results) {
|