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.
@@ -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
@@ -157,6 +156,16 @@ declare const readmeMetadataTemplate: import("metascope").Template<{
157
156
  bin: string[] | undefined;
158
157
  ciActionFileName: string | undefined;
159
158
  description: string | undefined;
159
+ developmentDependencies: {
160
+ packageManagers: {
161
+ name: string;
162
+ version: string | undefined;
163
+ }[] | undefined;
164
+ runtimes: {
165
+ name: string;
166
+ version: string | undefined;
167
+ }[] | undefined;
168
+ } | undefined;
160
169
  engines: {
161
170
  [k: string]: string;
162
171
  } | undefined;
@@ -175,6 +184,7 @@ declare const readmeMetadataTemplate: import("metascope").Template<{
175
184
  projectDirectory: string | undefined;
176
185
  repositoryUrl: string | undefined;
177
186
  runtimePlatform: string[] | undefined;
187
+ usesGitLfs: boolean;
178
188
  usesPnpm: boolean;
179
189
  }>;
180
190
  type ReadmeMetadata = ReturnType<typeof readmeMetadataTemplate>;
@@ -189,6 +199,16 @@ declare function getReadmeMetadata(): Promise<{
189
199
  bin: string[] | undefined;
190
200
  ciActionFileName: string | undefined;
191
201
  description: string | undefined;
202
+ developmentDependencies: {
203
+ packageManagers: {
204
+ name: string;
205
+ version: string | undefined;
206
+ }[] | undefined;
207
+ runtimes: {
208
+ name: string;
209
+ version: string | undefined;
210
+ }[] | undefined;
211
+ } | undefined;
192
212
  engines: {
193
213
  [k: string]: string;
194
214
  } | undefined;
@@ -207,6 +227,7 @@ declare function getReadmeMetadata(): Promise<{
207
227
  projectDirectory: string | undefined;
208
228
  repositoryUrl: string | undefined;
209
229
  runtimePlatform: string[] | undefined;
230
+ usesGitLfs: boolean;
210
231
  usesPnpm: boolean;
211
232
  }>;
212
233
  /**
package/dist/lib/index.js CHANGED
@@ -24,14 +24,15 @@ 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
  }, {});
32
32
  }
33
33
  function deepMergeDefined(...objects) {
34
- return deepmerge(...objects.map((v, i) => i === 0 ? v : stripUndefinedDeep(v)));
34
+ const stripped = objects.map((v, i) => i === 0 ? v : stripUndefinedDeep(v));
35
+ return deepmerge(...stripped);
35
36
  }
36
37
  //#endregion
37
38
  //#region src/lib/log.ts
@@ -70,7 +71,7 @@ function mdatJsonLoader(filePath, content) {
70
71
  }
71
72
  function flattenJson(jsonObject, parentKey = "", result = {}) {
72
73
  for (const [key, value] of Object.entries(jsonObject)) {
73
- const fullPath = parentKey ? `${parentKey}.${key}` : key;
74
+ const fullPath = parentKey === "" ? key : `${parentKey}.${key}`;
74
75
  if (typeof value === "object" && value !== null && !Array.isArray(value)) flattenJson(value, fullPath, result);
75
76
  else if (value === null) result[fullPath] = "null";
76
77
  else result[fullPath] = value.toString();
@@ -98,6 +99,7 @@ async function getContextMetadata() {
98
99
  "codemetaJson",
99
100
  "gitConfig",
100
101
  "githubActions",
102
+ "gitStats",
101
103
  "goGoMod",
102
104
  "goGoreleaserYaml",
103
105
  "javaPomXml",
@@ -124,9 +126,10 @@ async function getContextMetadata() {
124
126
  });
125
127
  return metascopeMetadata;
126
128
  }
127
- const GIT_PREFIX_REGEX = /^git\+/;
128
- const GIT_SUFFIX_REGEX = /\.git$/;
129
- const TRAILING_SLASH_REGEX = /\/$/;
129
+ const GIT_PREFIX_REGEX = /^git\+/v;
130
+ const GIT_SUFFIX_REGEX = /\.git$/v;
131
+ const TRAILING_SLASH_REGEX = /\/$/v;
132
+ const PACKAGE_MANAGER_REGEX = /^(?<name>[^@]+)@(?<version>[^+]+)/v;
130
133
  /**
131
134
  * Reset cached context metadata. Call between tests or when the underlying
132
135
  * project files may have changed on disk.
@@ -137,12 +140,12 @@ function resetContextMetadata() {
137
140
  metascopeMetadata = void 0;
138
141
  }
139
142
  const readmeMetadataTemplate = defineTemplate((context) => {
140
- const { githubActions, licenseFile, metascope, nodePackageJson } = context;
143
+ const { githubActions, gitStats, licenseFile, metascope, nodePackageJson } = context;
141
144
  const codemeta = templates.codemetaJson(context, {});
142
145
  const nodePackage = helpers.firstOf(nodePackageJson)?.data;
143
146
  const licenseFileData = helpers.firstOf(licenseFile);
144
147
  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, "");
148
+ const repoUrl = codemeta.codeRepository?.replace(GIT_PREFIX_REGEX, "").replace(GIT_SUFFIX_REGEX, "").replace(TRAILING_SLASH_REGEX, "");
146
149
  const bin = (() => {
147
150
  if (nodePackage === void 0) return;
148
151
  const binField = nodePackage.bin;
@@ -170,13 +173,35 @@ const readmeMetadataTemplate = defineTemplate((context) => {
170
173
  version
171
174
  }));
172
175
  })();
176
+ const developmentDependencies = (() => {
177
+ if (nodePackage === void 0) return;
178
+ const runtimes = helpers.ensureArray(nodePackage.devEngines?.runtime).map(({ name, version }) => ({
179
+ name,
180
+ version
181
+ }));
182
+ const packageManagers = helpers.ensureArray(nodePackage.devEngines?.packageManager).map(({ name, version }) => ({
183
+ name,
184
+ version
185
+ }));
186
+ const pinnedPackageManager = nodePackage.packageManager === void 0 ? void 0 : PACKAGE_MANAGER_REGEX.exec(nodePackage.packageManager)?.groups;
187
+ if (pinnedPackageManager?.name !== void 0 && pinnedPackageManager.version !== void 0 && packageManagers.every((entry) => entry.name !== pinnedPackageManager.name)) packageManagers.push({
188
+ name: pinnedPackageManager.name,
189
+ version: pinnedPackageManager.version
190
+ });
191
+ if (runtimes.length === 0 && packageManagers.length === 0) return;
192
+ return {
193
+ packageManagers: packageManagers.length > 0 ? packageManagers : void 0,
194
+ runtimes: runtimes.length > 0 ? runtimes : void 0
195
+ };
196
+ })();
173
197
  const firstAuthor = helpers.firstOf(helpers.ensureArray(codemeta.author));
174
198
  return {
175
199
  author: helpers.firstOf(helpers.mixedStringsToArray(helpers.toBasicNames(codemeta.author))),
176
200
  authorUrl: firstAuthor?.url,
177
201
  bin,
178
- ciActionFileName: ciActionFilePath ? path.basename(ciActionFilePath) : void 0,
202
+ ciActionFileName: ciActionFilePath === void 0 ? void 0 : path.basename(ciActionFilePath),
179
203
  description: codemeta.description,
204
+ developmentDependencies,
180
205
  engines,
181
206
  isPublicNpmPackage: nodePackage?.private !== true && (nodePackage?.name.startsWith("@") && nodePackage.publishConfig?.access === "public" || true),
182
207
  issuesUrl: codemeta.issueTracker,
@@ -187,8 +212,9 @@ const readmeMetadataTemplate = defineTemplate((context) => {
187
212
  operatingSystem: codemeta.operatingSystem,
188
213
  peerDependencies,
189
214
  projectDirectory: metascope?.data.options.path === void 0 ? void 0 : `file://${metascope.data.options.path}`,
190
- repositoryUrl,
215
+ repositoryUrl: repoUrl,
191
216
  runtimePlatform: codemeta.runtimePlatform,
217
+ usesGitLfs: helpers.firstOf(gitStats)?.data.hasLfs === true,
192
218
  usesPnpm: helpers.usesPnpm(nodePackageJson)
193
219
  };
194
220
  });
@@ -200,7 +226,8 @@ let readmeMetadata;
200
226
  */
201
227
  async function getReadmeMetadata() {
202
228
  if (readmeMetadata !== void 0) return readmeMetadata;
203
- readmeMetadata = readmeMetadataTemplate(await getContextMetadata(), {});
229
+ const contextMetadata = await getContextMetadata();
230
+ readmeMetadata = readmeMetadataTemplate(contextMetadata, {});
204
231
  return readmeMetadata;
205
232
  }
206
233
  /**
@@ -231,14 +258,15 @@ var badges_default = { badges: { async content(options) {
231
258
  npm: z.array(z.string()).optional()
232
259
  }).optional().parse(options);
233
260
  const metadata = await getReadmeMetadata();
234
- const { ciActionFileName, license, licenseUrl, name, repositoryUrl } = metadata;
261
+ const { ciActionFileName, license, licenseUrl, name, repositoryUrl, usesGitLfs } = metadata;
235
262
  const badges = [];
236
263
  if (validOptions?.npm === void 0) {
237
- if (metadata.isPublicNpmPackage) badges.push(`[![NPM Package ${name}](https://img.shields.io/npm/v/${name}.svg)](https://npmjs.com/package/${name})`);
238
- } else for (const name of validOptions.npm) badges.push(`[![NPM Package ${name}](https://img.shields.io/npm/v/${name}.svg)](https://npmjs.com/package/${name})`);
264
+ if (metadata.isPublicNpmPackage) badges.push(`[![NPM Package ${name}](https://img.shields.io/npm/v/${name}.svg)](https://www.npmjs.com/package/${name})`);
265
+ } else for (const packageName of validOptions.npm) badges.push(`[![NPM Package ${packageName}](https://img.shields.io/npm/v/${packageName}.svg)](https://www.npmjs.com/package/${packageName})`);
239
266
  if (license !== void 0 && licenseUrl !== void 0) badges.push(`[![License: ${license}](https://img.shields.io/badge/License-${license.replaceAll("-", "--")}-yellow.svg)](${licenseUrl})`);
240
267
  if (ciActionFileName !== void 0 && repositoryUrl !== void 0) badges.push(`[![CI](${repositoryUrl}/actions/workflows/${ciActionFileName}/badge.svg)](${repositoryUrl}/actions/workflows/${ciActionFileName})`);
241
- if (validOptions?.custom !== void 0) for (const [name, { image, link }] of Object.entries(validOptions.custom)) badges.push(`[![${name}](${image})](${link})`);
268
+ if (usesGitLfs) badges.push("![Git LFS](https://img.shields.io/badge/Git%20LFS-enabled-F64935?logo=gitlfs&logoColor=white)");
269
+ if (validOptions?.custom !== void 0) for (const [badgeName, { image, link }] of Object.entries(validOptions.custom)) badges.push(`[![${badgeName}](${image})](${link})`);
242
270
  return badges.join("\n");
243
271
  } } };
244
272
  //#endregion
@@ -250,7 +278,7 @@ var banner_default = { banner: { async content(options) {
250
278
  }).optional().parse(options);
251
279
  const src = validOptions?.src ?? await getBannerSrc();
252
280
  if (src === void 0) throw new Error("Banner image not found at any typical location, consider adding something at ./assets/banner.webp");
253
- else if (!isUrl(src) && !await isFile(src)) throw new Error(`Banner image not found at "${src}"`);
281
+ if (!isUrl(src) && !await isFile(src)) throw new Error(`Banner image not found at "${src}"`);
254
282
  let alt = validOptions?.alt;
255
283
  if (alt === void 0) {
256
284
  const { name: packageName } = await getReadmeMetadata();
@@ -262,7 +290,7 @@ var banner_default = { banner: { async content(options) {
262
290
  async function getBannerSrc() {
263
291
  const { projectDirectory } = await getReadmeMetadata();
264
292
  if (projectDirectory === void 0) throw new Error("No project directory found");
265
- const paths = await globby([
293
+ const [firstPath] = await globby([
266
294
  ".",
267
295
  "assets",
268
296
  "media",
@@ -298,19 +326,18 @@ async function getBannerSrc() {
298
326
  ]
299
327
  }
300
328
  });
301
- if (paths.length > 0) return path.relative(process.cwd(), paths[0]);
329
+ return firstPath === void 0 ? void 0 : path.relative(process.cwd(), firstPath);
302
330
  }
303
331
  function isUrl(text, lenient = true) {
304
332
  if (typeof text !== "string") throw new TypeError("Expected a string");
305
333
  text = text.trim();
306
334
  if (text.includes(" ")) return false;
307
335
  if (URL.canParse(text)) return true;
308
- if (lenient) return isUrl(`https://${text}`, false);
309
- return false;
336
+ return lenient && URL.canParse(`https://${text}`);
310
337
  }
311
338
  //#endregion
312
339
  //#region src/lib/readme/rules/code.ts
313
- const LEADING_DOT_REGEX = /^\./;
340
+ const LEADING_DOT_REGEX = /^\./v;
314
341
  var code_default = { code: { async content(options) {
315
342
  const validOptions = z.object({
316
343
  file: z.string(),
@@ -357,7 +384,7 @@ const PLATFORM_INFO = {
357
384
  },
358
385
  node: {
359
386
  display: "Node.js",
360
- url: "https://nodejs.org/"
387
+ url: "https://nodejs.org/en"
361
388
  },
362
389
  python: {
363
390
  display: "Python",
@@ -387,7 +414,7 @@ var dependencies_default = { dependencies: { async content() {
387
414
  if (engines?.[platformKey] !== void 0) continue;
388
415
  const info = PLATFORM_INFO[platformKey.toLowerCase()];
389
416
  const display = info ? `[${info.display}](${info.url})` : platformKey;
390
- platformItems.push(version ? `- ${display} ${version}` : `- ${display}`);
417
+ platformItems.push(version === void 0 || version === "" ? `- ${display}` : `- ${display} ${version}`);
391
418
  }
392
419
  if (operatingSystem !== void 0 && operatingSystem.length > 0) platformItems.push(`- Supported platforms: ${operatingSystem.join(", ")}`);
393
420
  const peerItems = [];
@@ -416,6 +443,56 @@ var description_default = { description: { async content() {
416
443
  return `**${description}**`;
417
444
  } } };
418
445
  //#endregion
446
+ //#region src/lib/readme/rules/development-dependencies.ts
447
+ const TOOL_INFO = {
448
+ bun: {
449
+ display: "Bun",
450
+ url: "https://bun.sh/"
451
+ },
452
+ deno: {
453
+ display: "Deno",
454
+ url: "https://deno.land/"
455
+ },
456
+ node: {
457
+ display: "Node.js",
458
+ url: "https://nodejs.org/en"
459
+ },
460
+ npm: {
461
+ display: "npm",
462
+ url: "https://www.npmjs.com/"
463
+ },
464
+ pnpm: {
465
+ display: "pnpm",
466
+ url: "https://pnpm.io/"
467
+ },
468
+ yarn: {
469
+ display: "Yarn",
470
+ url: "https://yarnpkg.com/"
471
+ }
472
+ };
473
+ var development_dependencies_default = { "development-dependencies": { async content() {
474
+ const { developmentDependencies } = await getReadmeMetadata();
475
+ if (developmentDependencies === void 0) return "";
476
+ const { packageManagers, runtimes } = developmentDependencies;
477
+ const items = [];
478
+ for (const { name, version } of [...runtimes ?? [], ...packageManagers ?? []]) {
479
+ const info = TOOL_INFO[name.toLowerCase()];
480
+ const display = info ? `[${info.display}](${info.url})` : name;
481
+ items.push(version === void 0 || version === "" ? `- ${display}` : `- ${display} ${version}`);
482
+ }
483
+ return [
484
+ "## Development Dependencies",
485
+ "",
486
+ ...items
487
+ ].join("\n");
488
+ } } };
489
+ //#endregion
490
+ //#region src/lib/readme/rules/dev-dependencies.ts
491
+ /**
492
+ * Simple alias for `development-dependencies`
493
+ */
494
+ var dev_dependencies_default = { "dev-dependencies": getSoleRule(development_dependencies_default) };
495
+ //#endregion
419
496
  //#region src/lib/readme/rules/license.ts
420
497
  var license_default = { license: { async content() {
421
498
  const { author, authorUrl, license, licenseFilePath } = await getReadmeMetadata();
@@ -449,7 +526,7 @@ var title_default = { title: {
449
526
  },
450
527
  order: 2
451
528
  } };
452
- const SPLIT_FOR_TITLE_CASE_REGEX = /[ _-]+/;
529
+ const SPLIT_FOR_TITLE_CASE_REGEX = /[ _\-]+/v;
453
530
  function makeTitleCase(text) {
454
531
  return text.split(SPLIT_FOR_TITLE_CASE_REGEX).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
455
532
  }
@@ -520,7 +597,7 @@ async function createSizeReport(filePath) {
520
597
  original: createSizeInfo(originalSize, originalSize)
521
598
  };
522
599
  } catch (error) {
523
- throw new Error(`Failed to analyze file: ${error instanceof Error ? error.message : "Unknown error"}`);
600
+ throw new Error(`Failed to analyze file: ${error instanceof Error ? error.message : "Unknown error"}`, { cause: error });
524
601
  }
525
602
  }
526
603
  //#endregion
@@ -603,20 +680,21 @@ var size_table_default = { "size-table": { async content(options) {
603
680
  //#region src/lib/readme/rules/table-of-contents.ts
604
681
  var table_of_contents_default = { "table-of-contents": {
605
682
  async content(options, { tree }) {
683
+ const validOptions = z.object({ depth: z.union([
684
+ z.literal(1),
685
+ z.literal(2),
686
+ z.literal(3),
687
+ z.literal(4),
688
+ z.literal(5),
689
+ z.literal(6)
690
+ ]).optional() }).optional().parse(options);
606
691
  const result = toc(tree, {
607
692
  heading: null,
608
- maxDepth: z.object({ depth: z.union([
609
- z.literal(1),
610
- z.literal(2),
611
- z.literal(3),
612
- z.literal(4),
613
- z.literal(5),
614
- z.literal(6)
615
- ]).optional() }).optional().parse(options)?.depth ?? 3,
693
+ maxDepth: validOptions?.depth ?? 3,
616
694
  tight: true
617
695
  });
618
- const heading = `## Table of contents`;
619
696
  if (result.map === void 0) throw new Error("Could not generate table of contents");
697
+ const heading = `## Table of contents`;
620
698
  const rootWrapper = {
621
699
  children: result.map.children,
622
700
  type: "root"
@@ -640,6 +718,8 @@ var rules_default = {
640
718
  ...contributing_default,
641
719
  ...dependencies_default,
642
720
  ...description_default,
721
+ ...dev_dependencies_default,
722
+ ...development_dependencies_default,
643
723
  ...footer_default,
644
724
  ...header_default,
645
725
  ...install_default,
@@ -674,13 +754,13 @@ function getAdditionalConfigExplorer() {
674
754
  async function loadConfig(options) {
675
755
  const { additionalConfig, defaults = rules_default, searchFrom } = options ?? {};
676
756
  let finalConfig = { mdat: `Powered by the Markdown Autophagic Template system: [mdat](https://github.com/kitschpatrol/mdat).` };
677
- if (defaults) finalConfig = deepMergeDefined(finalConfig, defaults);
757
+ finalConfig = deepMergeDefined(finalConfig, defaults);
678
758
  const results = await getConfigExplorer().search(searchFrom);
679
759
  if (results) {
680
760
  const { config, filepath } = results;
681
761
  let possibleRules = config;
682
762
  log.debug(`Using config from "${filepath}"`);
683
- if (filepath.endsWith("package.json") && typeof config === "string") {
763
+ if (typeof config === "string" && filepath.endsWith("package.json")) {
684
764
  log.debug(`Detected shared config string: "${config}"`);
685
765
  const { default: sharedConfig } = await import(config);
686
766
  possibleRules = sharedConfig;
@@ -693,14 +773,14 @@ async function loadConfig(options) {
693
773
  for (const configOrPath of additionalConfigArray) {
694
774
  let loaded;
695
775
  if (typeof configOrPath === "string") {
696
- let results;
697
- if (path.basename(configOrPath).endsWith("package.json")) results = {
776
+ let loadResults;
777
+ if (path.basename(configOrPath).endsWith("package.json")) loadResults = {
698
778
  config: mdatJsonLoader(configOrPath, await fs.readFile(configOrPath, "utf8")),
699
779
  filepath: configOrPath
700
780
  };
701
- else results = await getAdditionalConfigExplorer().load(configOrPath);
702
- if (results === null || results === void 0) continue;
703
- const { config: loadedConfig, filepath } = results;
781
+ else loadResults = await getAdditionalConfigExplorer().load(configOrPath);
782
+ if (loadResults === null || loadResults === void 0) continue;
783
+ const { config: loadedConfig, filepath } = loadResults;
704
784
  log.debug(`Loaded additional config from "${filepath}"`);
705
785
  loaded = loadedConfig;
706
786
  } else loaded = configOrPath;
@@ -745,7 +825,7 @@ async function formatWithPrettier(content, filePath) {
745
825
  } catch {
746
826
  throw new Error("The --format flag requires `prettier` to be installed. Run: pnpm add -D prettier");
747
827
  }
748
- const configKey = filePath ? path.dirname(filePath) : process.cwd();
828
+ const configKey = filePath === void 0 || filePath === "" ? process.cwd() : path.dirname(filePath);
749
829
  let config = configCache.get(configKey);
750
830
  if (config === void 0 && !configCache.has(configKey)) {
751
831
  config = await cachedPrettier.resolveConfig(filePath ?? process.cwd());
@@ -767,22 +847,22 @@ function zeroPad(n, nMax) {
767
847
  async function getInputOutputPaths(inputs, output, name, extension) {
768
848
  const paths = [];
769
849
  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)}` : "");
850
+ const inputOutputPath = await getInputOutputPath(file, output, name, extension, name !== void 0 && name !== "" && inputs.length > 1 ? `-${zeroPad(index + 1, inputs.length)}` : "");
771
851
  paths.push(inputOutputPath);
772
852
  }
773
853
  return paths;
774
854
  }
775
855
  async function getInputOutputPath(input, output, name, extension, nameSuffix = "") {
776
856
  const resolvedInput = expandPath(input);
777
- const resolvedOutput = output ? expandPath(output) : void 0;
778
857
  if (!isFileSync(resolvedInput)) throw new Error(`Input file not found: "${resolvedInput}"`);
779
- if (resolvedOutput) {
858
+ const resolvedOutput = output === void 0 || output === "" ? void 0 : expandPath(output);
859
+ if (resolvedOutput !== void 0) {
780
860
  if (isFileSync(resolvedOutput)) throw new Error(`Output path must be a directory, received a file path: "${resolvedOutput}"`);
781
861
  await fs.mkdir(resolvedOutput, { recursive: true });
782
862
  }
783
863
  return {
784
864
  input: resolvedInput,
785
- name: `${name ? path.basename(name, path.extname(name)) : path.basename(resolvedInput, path.extname(resolvedInput))}${nameSuffix}${extension === void 0 ? name && path.extname(name) !== "" ? path.extname(name) : path.extname(input) : `.${extension}`}`,
865
+ 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
866
  output: resolvedOutput ?? path.dirname(resolvedInput)
787
867
  };
788
868
  }
@@ -793,7 +873,7 @@ function ensureArray(value) {
793
873
  if (value === void 0 || value === null) return [];
794
874
  return Array.isArray(value) ? value : [value];
795
875
  }
796
- const README_SEARCH_REGEX = /^readme(?:\.\w+)?$/i;
876
+ const README_SEARCH_REGEX = /^readme(?:\.\w+)?$/iv;
797
877
  /**
798
878
  * Finds a readme file in the current working directory (case-insensitive).
799
879
  */
@@ -869,20 +949,20 @@ function stripLintPlugins(config) {
869
949
  //#endregion
870
950
  //#region src/lib/processors.ts
871
951
  async function processFiles(files, loader, processorGetter, name, output, config) {
872
- const [resolvedConfig, localRemarkConfiguration] = await Promise.all([loader({ additionalConfig: config }), loadAmbientRemarkConfig()]);
952
+ const [resolvedConfig, localRemarkConfig] = await Promise.all([loader({ additionalConfig: config }), loadAmbientRemarkConfig()]);
873
953
  const inputOutputPaths = await getInputOutputPaths(ensureArray(files), output, name, "md");
874
- const resolvedProcessor = processorGetter(resolvedConfig, localRemarkConfiguration);
875
- return await Promise.all(inputOutputPaths.map(async ({ input, name, output }) => {
954
+ const resolvedProcessor = processorGetter(resolvedConfig, localRemarkConfig);
955
+ return await Promise.all(inputOutputPaths.map(async ({ input, name: outputName, output: outputDirectory }) => {
876
956
  const inputFile = await read(input);
877
957
  const result = await resolvedProcessor.process(inputFile);
878
- result.dirname = output;
879
- result.basename = name;
958
+ result.dirname = outputDirectory;
959
+ result.basename = outputName;
880
960
  return result;
881
961
  }));
882
962
  }
883
963
  async function processString(markdown, loader, processorGetter, config) {
884
- const [resolvedConfig, localRemarkConfiguration] = await Promise.all([loader({ additionalConfig: config }), loadAmbientRemarkConfig()]);
885
- return processorGetter(resolvedConfig, localRemarkConfiguration).process(new VFile(markdown));
964
+ const [resolvedConfig, localRemarkConfig] = await Promise.all([loader({ additionalConfig: config }), loadAmbientRemarkConfig()]);
965
+ return processorGetter(resolvedConfig, localRemarkConfig).process(new VFile(markdown));
886
966
  }
887
967
  function getExpandProcessor(config, ambientRemarkConfig) {
888
968
  return remark().use({ settings: {
@@ -951,13 +1031,15 @@ async function createReadmeInteractive() {
951
1031
  const readmePath = await findReadme();
952
1032
  intro(`Running ${picocolors.bold("mdat create")} interactively`);
953
1033
  const newReadmePath = await createReadme(await group({
954
- overwrite: async () => readmePath === void 0 ? true : await confirm({
955
- message: `Found an existing readme at "${picocolors.blue(readmePath)}". Do you want to overwrite it?`,
956
- active: "Overwrite",
957
- inactive: "Exit"
958
- }) ? true : (() => {
959
- throw new Error("`mdat create` was cancelled to avoid an overwrite - no changes were made");
960
- })(),
1034
+ async overwrite() {
1035
+ if (readmePath === void 0) return true;
1036
+ if (await confirm({
1037
+ message: `Found an existing readme at "${picocolors.blue(readmePath)}". Do you want to overwrite it?`,
1038
+ active: "Overwrite",
1039
+ inactive: "Exit"
1040
+ }) === false) throw new Error("`mdat create` was cancelled to avoid an overwrite - no changes were made");
1041
+ return true;
1042
+ },
961
1043
  template: async () => select({
962
1044
  message: "Which template would you like to use?",
963
1045
  options: getTemplateOptions()
@@ -999,17 +1081,24 @@ async function createReadme(options) {
999
1081
  const templateString = getTemplateForConfig(resolvedOptions.template, resolvedOptions.compound);
1000
1082
  const readmePath = path.join(resolvedOptions.output, "readme.md");
1001
1083
  if (!resolvedOptions.overwrite) {
1002
- if (await fs.access(readmePath).then(() => true, () => false)) throw new Error(`Readme already exists at "${readmePath}". Use the overwrite option to replace it.`);
1084
+ let exists = true;
1085
+ try {
1086
+ await fs.access(readmePath);
1087
+ } catch {
1088
+ exists = false;
1089
+ }
1090
+ if (exists) throw new Error(`Readme already exists at "${readmePath}". Use the overwrite option to replace it.`);
1003
1091
  }
1004
1092
  await fs.writeFile(readmePath, templateString, "utf8");
1005
1093
  if (resolvedOptions.expand) {
1006
1094
  const [result] = await expand(readmePath);
1095
+ if (result === void 0) throw new Error(`Failed to expand readme at "${readmePath}"`);
1007
1096
  await write(result);
1008
1097
  }
1009
1098
  return readmePath;
1010
1099
  }
1011
1100
  function getTemplateForConfig(templateKey, compound) {
1012
- if (!(templateKey in templates_default)) throw new Error(`Unknown template "${templateKey}". Available templates: ${Object.keys(templates_default).join(", ")}`);
1101
+ if (!Object.hasOwn(templates_default, templateKey)) throw new Error(`Unknown template "${templateKey}". Available templates: ${Object.keys(templates_default).join(", ")}`);
1013
1102
  return templates_default[templateKey].content[compound ? "compound" : "explicit"];
1014
1103
  }
1015
1104
  function getTemplateOptions() {
@@ -1097,7 +1186,11 @@ async function check(files, config, options) {
1097
1186
  const resolvedFiles = Array.isArray(files) ? files : [files];
1098
1187
  const [originals, results] = await Promise.all([Promise.all(resolvedFiles.map(async (f) => read(f))), processFiles(files, loadConfig, getExpandProcessor, void 0, void 0, config)]);
1099
1188
  if (options?.format) await formatResults(results);
1100
- return results.map((result, i) => compareWithDiff(originals[i], result, options));
1189
+ return results.map((result, i) => {
1190
+ const original = originals[i];
1191
+ if (original === void 0) throw new Error(`Missing original file for comparison at index ${i}`);
1192
+ return compareWithDiff(original, result, options);
1193
+ });
1101
1194
  }
1102
1195
  /**
1103
1196
  * Check if MDAT comments in a Markdown string are up to date by expanding and
@@ -1130,7 +1223,7 @@ function compareWithDiff(original, result, options) {
1130
1223
  };
1131
1224
  }
1132
1225
  async function formatResults(results) {
1133
- for (const file of results) file.value = await formatWithPrettier(file.toString(), file.path || void 0);
1226
+ for (const file of results) file.value = await formatWithPrettier(file.toString(), file.path === "" ? void 0 : file.path);
1134
1227
  }
1135
1228
  //#endregion
1136
1229
  export { check, checkString, collapse, collapseString, createReadme as create, createReadmeInteractive as createInteractive, defineConfig, expand, expandString, getContextMetadata, getReadmeMetadata, loadConfig, mergeConfig, resetMetadataCaches, setLogger, strip, stripString };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mdat",
3
- "version": "2.3.6",
3
+ "version": "3.1.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,51 +43,51 @@
43
43
  "dist/*"
44
44
  ],
45
45
  "dependencies": {
46
- "@clack/prompts": "^1.5.1",
47
- "cosmiconfig": "^9.0.1",
46
+ "@clack/prompts": "^1.7.0",
47
+ "cosmiconfig": "^10.0.0",
48
48
  "cosmiconfig-typescript-loader": "^6.3.0",
49
49
  "deepmerge-ts": "^7.1.5",
50
- "globby": "^16.2.0",
51
- "lognow": "^0.6.2",
50
+ "globby": "^16.2.2",
51
+ "lognow": "^0.8.0",
52
52
  "mdast-util-toc": "^7.1.0",
53
- "metascope": "^0.8.1",
53
+ "metascope": "^0.10.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.0",
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": "^2.2.4",
61
+ "remark-mdat": "^3.0.1",
62
62
  "to-vfile": "^8.0.0",
63
- "type-fest": "^5.7.0",
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",
67
- "yargs": "^18.0.0",
67
+ "yargs": "^18.1.0",
68
68
  "zod": "^4.4.3"
69
69
  },
70
70
  "devDependencies": {
71
- "@arethetypeswrong/core": "^0.18.3",
72
- "@kitschpatrol/shared-config": "^7.6.3",
71
+ "@arethetypeswrong/core": "^0.18.5",
72
+ "@kitschpatrol/shared-config": "^8.6.0",
73
73
  "@types/mdast": "^4.0.4",
74
- "@types/node": "~22.19.19",
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.8",
78
- "bumpp": "^11.1.0",
79
- "execa": "^9.6.1",
80
- "mdat-plugin-cli-help": "^3.0.1",
81
- "mdat-plugin-example": "^2.0.1",
82
- "mdat-plugin-tldraw": "^2.0.2",
83
- "nanoid": "^5.1.11",
84
- "prettier": "^3.8.3",
85
- "publint": "^0.3.21",
77
+ "@vitest/coverage-v8": "^4.1.10",
78
+ "bumpp": "^12.1.1",
79
+ "execa": "^10.0.1",
80
+ "mdat-plugin-cli-help": "^3.1.0",
81
+ "mdat-plugin-example": "^3.0.0",
82
+ "mdat-plugin-tldraw": "^3.0.0",
83
+ "nanoid": "^6.0.1",
84
+ "prettier": "^3.9.6",
85
+ "publint": "^0.3.22",
86
86
  "shx": "^0.4.0",
87
- "tsdown": "^0.22.2",
88
- "typescript": "~5.9.3",
87
+ "tsdown": "^0.22.14",
88
+ "typescript": "~6.0.3",
89
89
  "unplugin-raw": "^0.7.0",
90
- "vitest": "^4.1.8"
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": ">=22.17.0"
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",