mdat 2.2.1 → 2.3.1

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 CHANGED
@@ -42,7 +42,7 @@ function deepMergeDefined(...objects) {
42
42
  //#endregion
43
43
  //#region package.json
44
44
  var name = "mdat";
45
- var version = "2.2.1";
45
+ var version = "2.3.1";
46
46
 
47
47
  //#endregion
48
48
  //#region src/lib/log.ts
@@ -56,9 +56,11 @@ let log = createLogger({
56
56
  setLogger(getChildLogger(log, "remark-mdat"));
57
57
  setLogger$1(getChildLogger(log, "metascope"));
58
58
  /**
59
- * Set the logger instance for the module.
60
- * Export this for library consumers to inject their own logger.
61
- * @param logger - Accepts either a LogLayer instance or a Console- or Stream-like log target
59
+ * Set the logger instance for the module. Export this for library consumers to
60
+ * inject their own logger.
61
+ *
62
+ * @param logger - Accepts either a LogLayer instance or a Console- or
63
+ * Stream-like log target
62
64
  */
63
65
  function setLogger$2(logger) {
64
66
  log = injectionHelper(logger);
@@ -69,9 +71,10 @@ function setLogger$2(logger) {
69
71
  //#endregion
70
72
  //#region src/lib/mdat-json-loader.ts
71
73
  /**
72
- * Lets arbitrary JSON objects (like from package.json) become reasonably good mdat rule sets
73
- * HOWEVER cosmiconfig treats package.json as a special case and will always load only specific keys from it
74
- * So we have to intercept and load them manually in config.ts
74
+ * Lets arbitrary JSON objects (like from package.json) become reasonably good
75
+ * mdat rule sets HOWEVER cosmiconfig treats package.json as a special case and
76
+ * will always load only specific keys from it So we have to intercept and load
77
+ * them manually in config.ts
75
78
  */
76
79
  function mdatJsonLoader(filePath, content) {
77
80
  const defaultJsonLoader = defaultLoaders[".json"];
@@ -144,17 +147,53 @@ const readmeMetadataTemplate = defineTemplate((context) => {
144
147
  const licenseFileData = helpers.firstOf(licenseFile);
145
148
  const ciActionFilePath = helpers.ensureArray(githubActions).find((entry) => entry.data.name.toLowerCase() === "ci")?.source;
146
149
  const repositoryUrl = codemeta.codeRepository?.replace(GIT_PREFIX_REGEX, "").replace(GIT_SUFFIX_REGEX, "").replace(TRAILING_SLASH_REGEX, "");
150
+ const bin = (() => {
151
+ if (nodePackage === void 0) return;
152
+ const binField = nodePackage.bin;
153
+ if (binField === void 0) return;
154
+ if (typeof binField === "string") return [nodePackage.name];
155
+ const names = Object.keys(binField);
156
+ return names.length > 0 ? names : void 0;
157
+ })();
158
+ const engines = (() => {
159
+ const raw = nodePackage?.engines;
160
+ if (raw === void 0) return;
161
+ const entries = Object.entries(raw).filter((entry) => entry[1] !== void 0);
162
+ return entries.length > 0 ? Object.fromEntries(entries) : void 0;
163
+ })();
164
+ const peerDependencies = (() => {
165
+ if (nodePackage === void 0) return;
166
+ const peers = nodePackage.peerDependencies;
167
+ if (peers === void 0) return;
168
+ const entries = Object.entries(peers).filter((entry) => entry[1] !== void 0);
169
+ if (entries.length === 0) return;
170
+ const meta = nodePackage.peerDependenciesMeta;
171
+ return entries.map(([name, version]) => ({
172
+ name,
173
+ optional: meta?.[name]?.optional === true,
174
+ version
175
+ }));
176
+ })();
177
+ const firstAuthor = helpers.firstOf(helpers.ensureArray(codemeta.author));
147
178
  return {
148
179
  author: helpers.firstOf(helpers.mixedStringsToArray(helpers.toBasicNames(codemeta.author))),
180
+ authorUrl: firstAuthor?.url,
181
+ bin,
149
182
  ciActionFileName: ciActionFilePath ? path.basename(ciActionFilePath) : void 0,
150
183
  description: codemeta.description,
151
- isPublicNpmPackage: !nodePackage?.name.startsWith("@") || nodePackage.publishConfig?.access === "public",
184
+ engines,
185
+ isPublicNpmPackage: nodePackage?.private !== true && (nodePackage?.name.startsWith("@") && nodePackage.publishConfig?.access === "public" || true),
152
186
  issuesUrl: codemeta.issueTracker,
153
187
  license: helpers.toBasicLicense(helpers.firstOf(helpers.ensureArray(codemeta.license))),
154
188
  licenseFilePath: licenseFileData?.source,
189
+ licenseUrl: licenseFileData?.data.spdxUrl,
155
190
  name: codemeta.name,
191
+ operatingSystem: codemeta.operatingSystem,
192
+ peerDependencies,
156
193
  projectDirectory: metascope?.data.options.path === void 0 ? void 0 : `file://${metascope.data.options.path}`,
157
- repositoryUrl
194
+ repositoryUrl,
195
+ runtimePlatform: codemeta.runtimePlatform,
196
+ usesPnpm: helpers.usesPnpm(nodePackageJson)
158
197
  };
159
198
  });
160
199
  let readmeMetadata;
@@ -180,12 +219,12 @@ var badges_default = { badges: { async content(options) {
180
219
  npm: z.array(z.string()).optional()
181
220
  }).optional().parse(options);
182
221
  const metadata = await getReadmeMetadata();
183
- const { ciActionFileName, license, name, repositoryUrl } = metadata;
222
+ const { ciActionFileName, license, licenseUrl, name, repositoryUrl } = metadata;
184
223
  const badges = [];
185
224
  if (validOptions?.npm === void 0) {
186
225
  if (metadata.isPublicNpmPackage) badges.push(`[![NPM Package ${name}](https://img.shields.io/npm/v/${name}.svg)](https://npmjs.com/package/${name})`);
187
226
  } 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})`);
188
- if (license !== void 0) badges.push(`[![License: ${license}](https://img.shields.io/badge/License-${license.replaceAll("-", "--")}-yellow.svg)](https://opensource.org/licenses/${license})`);
227
+ if (license !== void 0 && licenseUrl !== void 0) badges.push(`[![License: ${license}](https://img.shields.io/badge/License-${license.replaceAll("-", "--")}-yellow.svg)](${licenseUrl})`);
189
228
  if (ciActionFileName !== void 0 && repositoryUrl !== void 0) badges.push(`[![CI](${repositoryUrl}/actions/workflows/${ciActionFileName}/badge.svg)](${repositoryUrl}/actions/workflows/${ciActionFileName})`);
190
229
  if (validOptions?.custom !== void 0) for (const [name, { image, link }] of Object.entries(validOptions.custom)) badges.push(`[![${name}](${image})](${link})`);
191
230
  return badges.join("\n");
@@ -278,7 +317,85 @@ var code_default = { code: { async content(options) {
278
317
  var contributing_default = { contributing: { async content() {
279
318
  const { issuesUrl } = await getReadmeMetadata();
280
319
  if (issuesUrl === void 0) throw new Error("Could not find \"bugs.url\" entry in package.json");
281
- return `## Contributing\n[Issues](${issuesUrl}) and pull requests are welcome.`;
320
+ return [
321
+ "## Contributing",
322
+ "",
323
+ `[Issues](${issuesUrl}) are welcome and appreciated.`,
324
+ "",
325
+ "Please open an issue to discuss changes before submitting a pull request. Unsolicited PRs (especially AI-generated ones) are unlikely to be merged.",
326
+ "",
327
+ "This repository uses [@kitschpatrol/shared-config](https://github.com/kitschpatrol/shared-config) (via its `ksc` CLI) for linting and formatting, plus [MDAT](https://github.com/kitschpatrol/mdat) for readme placeholder expansion."
328
+ ].join("\n");
329
+ } } };
330
+
331
+ //#endregion
332
+ //#region src/lib/readme/rules/dependencies.ts
333
+ const PLATFORM_INFO = {
334
+ bun: {
335
+ display: "Bun",
336
+ url: "https://bun.sh/"
337
+ },
338
+ deno: {
339
+ display: "Deno",
340
+ url: "https://deno.land/"
341
+ },
342
+ go: {
343
+ display: "Go",
344
+ url: "https://go.dev/"
345
+ },
346
+ java: {
347
+ display: "Java",
348
+ url: "https://www.java.com/"
349
+ },
350
+ node: {
351
+ display: "Node.js",
352
+ url: "https://nodejs.org/"
353
+ },
354
+ python: {
355
+ display: "Python",
356
+ url: "https://www.python.org/"
357
+ },
358
+ ruby: {
359
+ display: "Ruby",
360
+ url: "https://www.ruby-lang.org/"
361
+ },
362
+ rust: {
363
+ display: "Rust",
364
+ url: "https://www.rust-lang.org/"
365
+ }
366
+ };
367
+ var dependencies_default = { dependencies: { async content() {
368
+ const { engines, operatingSystem, peerDependencies, runtimePlatform } = await getReadmeMetadata();
369
+ const platformItems = [];
370
+ if (engines !== void 0) for (const [name, version] of Object.entries(engines)) {
371
+ const info = PLATFORM_INFO[name.toLowerCase()];
372
+ const display = info ? `[${info.display}](${info.url})` : name;
373
+ platformItems.push(`- ${display} ${version}`);
374
+ }
375
+ if (runtimePlatform !== void 0) for (const entry of runtimePlatform) {
376
+ const spaceIndex = entry.indexOf(" ");
377
+ const platformKey = spaceIndex > 0 ? entry.slice(0, spaceIndex) : entry;
378
+ const version = spaceIndex > 0 ? entry.slice(spaceIndex + 1) : void 0;
379
+ if (engines?.[platformKey] !== void 0) continue;
380
+ const info = PLATFORM_INFO[platformKey.toLowerCase()];
381
+ const display = info ? `[${info.display}](${info.url})` : platformKey;
382
+ platformItems.push(version ? `- ${display} ${version}` : `- ${display}`);
383
+ }
384
+ if (operatingSystem !== void 0 && operatingSystem.length > 0) platformItems.push(`- Supported platforms: ${operatingSystem.join(", ")}`);
385
+ const peerItems = [];
386
+ if (peerDependencies !== void 0) for (const { name, optional, version } of peerDependencies) {
387
+ const npmUrl = `https://www.npmjs.com/package/${name}`;
388
+ const optionalSuffix = optional ? " _(optional)_" : "";
389
+ peerItems.push(`- [${name}](${npmUrl}) ${version}${optionalSuffix}`);
390
+ }
391
+ const hasPlatform = platformItems.length > 0;
392
+ const hasPeers = peerItems.length > 0;
393
+ if (!hasPlatform && !hasPeers) return "";
394
+ const sections = ["## Dependencies"];
395
+ if (hasPlatform && hasPeers) sections.push("", "### Platform", "", ...platformItems, "", "### Peer Dependencies", "", ...peerItems);
396
+ else if (hasPlatform) sections.push("", ...platformItems);
397
+ else sections.push("", ...peerItems);
398
+ return sections.join("\n");
282
399
  } } };
283
400
 
284
401
  //#endregion
@@ -295,10 +412,10 @@ var description_default = { description: { async content() {
295
412
  //#endregion
296
413
  //#region src/lib/readme/rules/license.ts
297
414
  var license_default = { license: { async content() {
298
- const { author, license, licenseFilePath } = await getReadmeMetadata();
415
+ const { author, authorUrl, license, licenseFilePath } = await getReadmeMetadata();
299
416
  if (author === void 0) throw new Error("Could not find author name in project");
300
417
  if (license === void 0 || licenseFilePath === void 0) throw new Error("Could not find license for project");
301
- return `## License\n[${license}](${licenseFilePath}) © ${author}`;
418
+ return `## License\n[${license}](${licenseFilePath}) © ${authorUrl === void 0 ? author : `[${author}](${authorUrl})`}`;
302
419
  } } };
303
420
 
304
421
  //#endregion
@@ -346,12 +463,32 @@ var header_default = { header: {
346
463
  order: 2
347
464
  } };
348
465
 
466
+ //#endregion
467
+ //#region src/lib/readme/rules/install.ts
468
+ var install_default = { install: { async content() {
469
+ const { bin, engines, isPublicNpmPackage, name, runtimePlatform, usesPnpm } = await getReadmeMetadata();
470
+ if (name === void 0) throw new Error("Could not find project name");
471
+ const lines = ["## Install"];
472
+ if (isPublicNpmPackage || engines?.node !== void 0 || runtimePlatform?.includes("node")) {
473
+ const pmAdd = usesPnpm ? "pnpm add" : "npm install";
474
+ const pmx = usesPnpm ? "pnpx" : "npx";
475
+ lines.push("", "```sh", `${pmAdd} ${name}`, "```");
476
+ if (bin !== void 0 && bin.length > 0) lines.push("", "Or run it directly:", "", "```sh", `${pmx} ${name}`, "```");
477
+ } else if (runtimePlatform?.some((p) => p.startsWith("python"))) lines.push("", "```sh", `pip install ${name}`, "```");
478
+ else if (runtimePlatform?.some((p) => p.startsWith("rust"))) lines.push("", "```sh", `cargo install ${name}`, "```");
479
+ else if (runtimePlatform?.some((p) => p.startsWith("go"))) lines.push("", "```sh", `go install ${name}@latest`, "```");
480
+ else if (runtimePlatform?.some((p) => p.startsWith("ruby"))) lines.push("", "```sh", `gem install ${name}`, "```");
481
+ else throw new Error("Could not determine project ecosystem for install instructions");
482
+ return lines.join("\n");
483
+ } } };
484
+
349
485
  //#endregion
350
486
  //#region src/lib/readme/rules/utilities/size/size-report.ts
351
487
  const brotliCompressAsync = promisify(brotliCompress);
352
488
  const gzipCompressAsync = promisify(gzip);
353
489
  /**
354
490
  * Creates a SizeInfo object with formatted values
491
+ *
355
492
  * @param bytes - Size in bytes
356
493
  * @param originalSize - Original file size for percentage calculation
357
494
  */
@@ -366,9 +503,11 @@ function createSizeInfo(bytes, originalSize) {
366
503
  }
367
504
  /**
368
505
  * Analyzes a file's size and its compressed sizes using Brotli and Gzip
506
+ *
369
507
  * @param filePath - Path to the file to analyze
508
+ *
370
509
  * @returns Promise containing detailed size report
371
- * @throws {Error} if file cannot be read or compressed
510
+ * @throws {Error} If file cannot be read or compressed
372
511
  */
373
512
  async function createSizeReport(filePath) {
374
513
  try {
@@ -504,9 +643,11 @@ var rules_default = {
504
643
  ...banner_default,
505
644
  ...code_default,
506
645
  ...contributing_default,
646
+ ...dependencies_default,
507
647
  ...description_default,
508
648
  ...footer_default,
509
649
  ...header_default,
650
+ ...install_default,
510
651
  ...license_default,
511
652
  ...short_description_default,
512
653
  ...size_default,
@@ -532,9 +673,9 @@ function getAdditionalConfigExplorer() {
532
673
  return _additionalConfigExplorer;
533
674
  }
534
675
  /**
535
- * Load and validate mdat configuration.
536
- * Uses cosmiconfig to search in the usual places.
537
- * Merge precedence: Base Defaults < Defaults < Searched Config < Additional Config
676
+ * Load and validate mdat configuration. Uses cosmiconfig to search in the usual
677
+ * places. Merge precedence: Base Defaults < Defaults < Searched Config <
678
+ * Additional Config
538
679
  */
539
680
  async function loadConfig(options) {
540
681
  const { additionalConfig, defaults = rules_default, searchFrom } = options ?? {};
@@ -590,8 +731,8 @@ function validateConfig(value) {
590
731
  let cachedPrettier;
591
732
  const configCache = /* @__PURE__ */ new Map();
592
733
  /**
593
- * Format a markdown string with Prettier, using config discovered from the file path.
594
- * Requires `prettier` to be installed as a peer dependency.
734
+ * Format a markdown string with Prettier, using config discovered from the file
735
+ * path. Requires `prettier` to be installed as a peer dependency.
595
736
  */
596
737
  async function formatWithPrettier(content, filePath) {
597
738
  if (cachedPrettier === void 0) try {
@@ -822,6 +963,7 @@ var templates_default = {
822
963
  //#region src/lib/readme/create.ts
823
964
  /**
824
965
  * Creates a new readme file interactively.
966
+ *
825
967
  * @returns Path to the created readme file.
826
968
  */
827
969
  async function createReadmeInteractive() {
@@ -862,6 +1004,7 @@ async function createReadmeInteractive() {
862
1004
  }
863
1005
  /**
864
1006
  * Creates a new readme file with the given options.
1007
+ *
865
1008
  * @returns Path to the created readme file.
866
1009
  */
867
1010
  async function createReadme(options) {
@@ -6,39 +6,43 @@ import { VFile } from "vfile";
6
6
 
7
7
  //#region src/lib/config.d.ts
8
8
  /**
9
- * An mdat configuration is a record of expansion rules.
10
- * Keys become comment keywords, values define the expansion content.
9
+ * An mdat configuration is a record of expansion rules. Keys become comment
10
+ * keywords, values define the expansion content.
11
11
  */
12
12
  type Config = Record<string, Rule$1>;
13
13
  /**
14
- * Generously accept either string paths to .ts, .js, or .json config files,
15
- * or inline Config objects.
14
+ * Generously accept either string paths to .ts, .js, or .json config files, or
15
+ * inline Config objects.
16
16
  */
17
17
  type ConfigToLoad = Array<Config | string> | Config | string;
18
18
  /**
19
- * Load and validate mdat configuration.
20
- * Uses cosmiconfig to search in the usual places.
21
- * Merge precedence: Base Defaults < Defaults < Searched Config < Additional Config
19
+ * Load and validate mdat configuration. Uses cosmiconfig to search in the usual
20
+ * places. Merge precedence: Base Defaults < Defaults < Searched Config <
21
+ * Additional Config
22
22
  */
23
23
  declare function loadConfig(options?: {
24
24
  /**
25
25
  * Additional Config objects to merge.
26
26
  *
27
- * Strings are treated as paths to `ts`, `js`, or `json` config files.
28
- * These will be dynamically loaded by Cosmiconfig.
29
- * Accepts an individual item, or an array. Objects in the array will be merged right to left.
27
+ * Strings are treated as paths to `ts`, `js`, or `json` config files. These
28
+ * will be dynamically loaded by Cosmiconfig. Accepts an individual item, or
29
+ * an array. Objects in the array will be merged right to left.
30
30
  */
31
31
  additionalConfig?: ConfigToLoad;
32
32
  /**
33
- * Default rules that have higher priority than base defaults but lower than searched config.
34
- * Defaults to the built-in readme rules. Pass `{}` to disable.
33
+ * Default rules that have higher priority than base defaults but lower than
34
+ * searched config. Defaults to the built-in readme rules. Pass `{}` to
35
+ * disable.
36
+ */
37
+ defaults?: Config;
38
+ /**
39
+ * Search for config in specific directories, mainly useful for testing.
40
+ * Cosmiconfig default search paths used if unset.
35
41
  */
36
- defaults?: Config; /** Search for config in specific directories, mainly useful for testing. Cosmiconfig default search paths used if unset. */
37
42
  searchFrom?: string;
38
43
  }): Promise<Config>;
39
44
  /**
40
- * Convenience function for merging configs.
41
- * Rightmost config takes precedence.
45
+ * Convenience function for merging configs. Rightmost config takes precedence.
42
46
  */
43
47
  declare function mergeConfig(a: Config, b: Config): Config;
44
48
  /**
@@ -56,11 +60,13 @@ type MdatReadmeCreateOptions = {
56
60
  };
57
61
  /**
58
62
  * Creates a new readme file interactively.
63
+ *
59
64
  * @returns Path to the created readme file.
60
65
  */
61
66
  declare function createReadmeInteractive(): Promise<string>;
62
67
  /**
63
68
  * Creates a new readme file with the given options.
69
+ *
64
70
  * @returns Path to the created readme file.
65
71
  */
66
72
  declare function createReadme(options?: Partial<MdatReadmeCreateOptions>): Promise<string>;
@@ -148,15 +154,29 @@ declare function checkString(markdown: string, config?: ConfigToLoad, options?:
148
154
  declare function getContextMetadata(): Promise<MetadataContext>;
149
155
  declare const readmeMetadataTemplate: _$metascope.Template<{
150
156
  author: string | undefined;
157
+ authorUrl: string | undefined;
158
+ bin: string[] | undefined;
151
159
  ciActionFileName: string | undefined;
152
160
  description: string | undefined;
161
+ engines: {
162
+ [k: string]: string;
163
+ } | undefined;
153
164
  isPublicNpmPackage: boolean;
154
165
  issuesUrl: string | undefined;
155
166
  license: string | undefined;
156
167
  licenseFilePath: string | undefined;
168
+ licenseUrl: string | undefined;
157
169
  name: string | undefined;
170
+ operatingSystem: string[] | undefined;
171
+ peerDependencies: {
172
+ name: string;
173
+ optional: boolean;
174
+ version: string;
175
+ }[] | undefined;
158
176
  projectDirectory: string | undefined;
159
177
  repositoryUrl: string | undefined;
178
+ runtimePlatform: string[] | undefined;
179
+ usesPnpm: boolean;
160
180
  }>;
161
181
  type ReadmeMetadata = ReturnType<typeof readmeMetadataTemplate>;
162
182
  /**
@@ -166,15 +186,29 @@ type ReadmeMetadata = ReturnType<typeof readmeMetadataTemplate>;
166
186
  */
167
187
  declare function getReadmeMetadata(): Promise<{
168
188
  author: string | undefined;
189
+ authorUrl: string | undefined;
190
+ bin: string[] | undefined;
169
191
  ciActionFileName: string | undefined;
170
192
  description: string | undefined;
193
+ engines: {
194
+ [k: string]: string;
195
+ } | undefined;
171
196
  isPublicNpmPackage: boolean;
172
197
  issuesUrl: string | undefined;
173
198
  license: string | undefined;
174
199
  licenseFilePath: string | undefined;
200
+ licenseUrl: string | undefined;
175
201
  name: string | undefined;
202
+ operatingSystem: string[] | undefined;
203
+ peerDependencies: {
204
+ name: string;
205
+ optional: boolean;
206
+ version: string;
207
+ }[] | undefined;
176
208
  projectDirectory: string | undefined;
177
209
  repositoryUrl: string | undefined;
210
+ runtimePlatform: string[] | undefined;
211
+ usesPnpm: boolean;
178
212
  }>;
179
213
  /**
180
214
  * Reset all cached metadata. Call between tests or when the underlying project
@@ -184,10 +218,12 @@ declare function resetMetadataCaches(): void;
184
218
  //#endregion
185
219
  //#region src/lib/log.d.ts
186
220
  /**
187
- * Set the logger instance for the module.
188
- * Export this for library consumers to inject their own logger.
189
- * @param logger - Accepts either a LogLayer instance or a Console- or Stream-like log target
221
+ * Set the logger instance for the module. Export this for library consumers to
222
+ * inject their own logger.
223
+ *
224
+ * @param logger - Accepts either a LogLayer instance or a Console- or
225
+ * Stream-like log target
190
226
  */
191
- declare function setLogger(logger?: ILogBasic | ILogLayer): void;
227
+ declare function setLogger(logger?: ILogBasic | ILogLayer<unknown>): void;
192
228
  //#endregion
193
229
  export { type Config, type ReadmeMetadata, type Rule, check, checkString, collapse, collapseString, createReadme as create, createReadmeInteractive as createInteractive, defineConfig, expand, expandString, getContextMetadata, getReadmeMetadata, loadConfig, mergeConfig, resetMetadataCaches, setLogger, strip, stripString };
package/dist/lib/index.js CHANGED
@@ -45,9 +45,11 @@ let log = createLogger({
45
45
  setLogger$1(getChildLogger(log, "remark-mdat"));
46
46
  setLogger$2(getChildLogger(log, "metascope"));
47
47
  /**
48
- * Set the logger instance for the module.
49
- * Export this for library consumers to inject their own logger.
50
- * @param logger - Accepts either a LogLayer instance or a Console- or Stream-like log target
48
+ * Set the logger instance for the module. Export this for library consumers to
49
+ * inject their own logger.
50
+ *
51
+ * @param logger - Accepts either a LogLayer instance or a Console- or
52
+ * Stream-like log target
51
53
  */
52
54
  function setLogger(logger) {
53
55
  log = injectionHelper(logger);
@@ -57,9 +59,10 @@ function setLogger(logger) {
57
59
  //#endregion
58
60
  //#region src/lib/mdat-json-loader.ts
59
61
  /**
60
- * Lets arbitrary JSON objects (like from package.json) become reasonably good mdat rule sets
61
- * HOWEVER cosmiconfig treats package.json as a special case and will always load only specific keys from it
62
- * So we have to intercept and load them manually in config.ts
62
+ * Lets arbitrary JSON objects (like from package.json) become reasonably good
63
+ * mdat rule sets HOWEVER cosmiconfig treats package.json as a special case and
64
+ * will always load only specific keys from it So we have to intercept and load
65
+ * them manually in config.ts
63
66
  */
64
67
  function mdatJsonLoader(filePath, content) {
65
68
  const defaultJsonLoader = defaultLoaders[".json"];
@@ -140,17 +143,53 @@ const readmeMetadataTemplate = defineTemplate((context) => {
140
143
  const licenseFileData = helpers.firstOf(licenseFile);
141
144
  const ciActionFilePath = helpers.ensureArray(githubActions).find((entry) => entry.data.name.toLowerCase() === "ci")?.source;
142
145
  const repositoryUrl = codemeta.codeRepository?.replace(GIT_PREFIX_REGEX, "").replace(GIT_SUFFIX_REGEX, "").replace(TRAILING_SLASH_REGEX, "");
146
+ const bin = (() => {
147
+ if (nodePackage === void 0) return;
148
+ const binField = nodePackage.bin;
149
+ if (binField === void 0) return;
150
+ if (typeof binField === "string") return [nodePackage.name];
151
+ const names = Object.keys(binField);
152
+ return names.length > 0 ? names : void 0;
153
+ })();
154
+ const engines = (() => {
155
+ const raw = nodePackage?.engines;
156
+ if (raw === void 0) return;
157
+ const entries = Object.entries(raw).filter((entry) => entry[1] !== void 0);
158
+ return entries.length > 0 ? Object.fromEntries(entries) : void 0;
159
+ })();
160
+ const peerDependencies = (() => {
161
+ if (nodePackage === void 0) return;
162
+ const peers = nodePackage.peerDependencies;
163
+ if (peers === void 0) return;
164
+ const entries = Object.entries(peers).filter((entry) => entry[1] !== void 0);
165
+ if (entries.length === 0) return;
166
+ const meta = nodePackage.peerDependenciesMeta;
167
+ return entries.map(([name, version]) => ({
168
+ name,
169
+ optional: meta?.[name]?.optional === true,
170
+ version
171
+ }));
172
+ })();
173
+ const firstAuthor = helpers.firstOf(helpers.ensureArray(codemeta.author));
143
174
  return {
144
175
  author: helpers.firstOf(helpers.mixedStringsToArray(helpers.toBasicNames(codemeta.author))),
176
+ authorUrl: firstAuthor?.url,
177
+ bin,
145
178
  ciActionFileName: ciActionFilePath ? path.basename(ciActionFilePath) : void 0,
146
179
  description: codemeta.description,
147
- isPublicNpmPackage: !nodePackage?.name.startsWith("@") || nodePackage.publishConfig?.access === "public",
180
+ engines,
181
+ isPublicNpmPackage: nodePackage?.private !== true && (nodePackage?.name.startsWith("@") && nodePackage.publishConfig?.access === "public" || true),
148
182
  issuesUrl: codemeta.issueTracker,
149
183
  license: helpers.toBasicLicense(helpers.firstOf(helpers.ensureArray(codemeta.license))),
150
184
  licenseFilePath: licenseFileData?.source,
185
+ licenseUrl: licenseFileData?.data.spdxUrl,
151
186
  name: codemeta.name,
187
+ operatingSystem: codemeta.operatingSystem,
188
+ peerDependencies,
152
189
  projectDirectory: metascope?.data.options.path === void 0 ? void 0 : `file://${metascope.data.options.path}`,
153
- repositoryUrl
190
+ repositoryUrl,
191
+ runtimePlatform: codemeta.runtimePlatform,
192
+ usesPnpm: helpers.usesPnpm(nodePackageJson)
154
193
  };
155
194
  });
156
195
  let readmeMetadata;
@@ -192,12 +231,12 @@ var badges_default = { badges: { async content(options) {
192
231
  npm: z.array(z.string()).optional()
193
232
  }).optional().parse(options);
194
233
  const metadata = await getReadmeMetadata();
195
- const { ciActionFileName, license, name, repositoryUrl } = metadata;
234
+ const { ciActionFileName, license, licenseUrl, name, repositoryUrl } = metadata;
196
235
  const badges = [];
197
236
  if (validOptions?.npm === void 0) {
198
237
  if (metadata.isPublicNpmPackage) badges.push(`[![NPM Package ${name}](https://img.shields.io/npm/v/${name}.svg)](https://npmjs.com/package/${name})`);
199
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})`);
200
- if (license !== void 0) badges.push(`[![License: ${license}](https://img.shields.io/badge/License-${license.replaceAll("-", "--")}-yellow.svg)](https://opensource.org/licenses/${license})`);
239
+ if (license !== void 0 && licenseUrl !== void 0) badges.push(`[![License: ${license}](https://img.shields.io/badge/License-${license.replaceAll("-", "--")}-yellow.svg)](${licenseUrl})`);
201
240
  if (ciActionFileName !== void 0 && repositoryUrl !== void 0) badges.push(`[![CI](${repositoryUrl}/actions/workflows/${ciActionFileName}/badge.svg)](${repositoryUrl}/actions/workflows/${ciActionFileName})`);
202
241
  if (validOptions?.custom !== void 0) for (const [name, { image, link }] of Object.entries(validOptions.custom)) badges.push(`[![${name}](${image})](${link})`);
203
242
  return badges.join("\n");
@@ -287,7 +326,84 @@ var code_default = { code: { async content(options) {
287
326
  var contributing_default = { contributing: { async content() {
288
327
  const { issuesUrl } = await getReadmeMetadata();
289
328
  if (issuesUrl === void 0) throw new Error("Could not find \"bugs.url\" entry in package.json");
290
- return `## Contributing\n[Issues](${issuesUrl}) and pull requests are welcome.`;
329
+ return [
330
+ "## Contributing",
331
+ "",
332
+ `[Issues](${issuesUrl}) are welcome and appreciated.`,
333
+ "",
334
+ "Please open an issue to discuss changes before submitting a pull request. Unsolicited PRs (especially AI-generated ones) are unlikely to be merged.",
335
+ "",
336
+ "This repository uses [@kitschpatrol/shared-config](https://github.com/kitschpatrol/shared-config) (via its `ksc` CLI) for linting and formatting, plus [MDAT](https://github.com/kitschpatrol/mdat) for readme placeholder expansion."
337
+ ].join("\n");
338
+ } } };
339
+ //#endregion
340
+ //#region src/lib/readme/rules/dependencies.ts
341
+ const PLATFORM_INFO = {
342
+ bun: {
343
+ display: "Bun",
344
+ url: "https://bun.sh/"
345
+ },
346
+ deno: {
347
+ display: "Deno",
348
+ url: "https://deno.land/"
349
+ },
350
+ go: {
351
+ display: "Go",
352
+ url: "https://go.dev/"
353
+ },
354
+ java: {
355
+ display: "Java",
356
+ url: "https://www.java.com/"
357
+ },
358
+ node: {
359
+ display: "Node.js",
360
+ url: "https://nodejs.org/"
361
+ },
362
+ python: {
363
+ display: "Python",
364
+ url: "https://www.python.org/"
365
+ },
366
+ ruby: {
367
+ display: "Ruby",
368
+ url: "https://www.ruby-lang.org/"
369
+ },
370
+ rust: {
371
+ display: "Rust",
372
+ url: "https://www.rust-lang.org/"
373
+ }
374
+ };
375
+ var dependencies_default = { dependencies: { async content() {
376
+ const { engines, operatingSystem, peerDependencies, runtimePlatform } = await getReadmeMetadata();
377
+ const platformItems = [];
378
+ if (engines !== void 0) for (const [name, version] of Object.entries(engines)) {
379
+ const info = PLATFORM_INFO[name.toLowerCase()];
380
+ const display = info ? `[${info.display}](${info.url})` : name;
381
+ platformItems.push(`- ${display} ${version}`);
382
+ }
383
+ if (runtimePlatform !== void 0) for (const entry of runtimePlatform) {
384
+ const spaceIndex = entry.indexOf(" ");
385
+ const platformKey = spaceIndex > 0 ? entry.slice(0, spaceIndex) : entry;
386
+ const version = spaceIndex > 0 ? entry.slice(spaceIndex + 1) : void 0;
387
+ if (engines?.[platformKey] !== void 0) continue;
388
+ const info = PLATFORM_INFO[platformKey.toLowerCase()];
389
+ const display = info ? `[${info.display}](${info.url})` : platformKey;
390
+ platformItems.push(version ? `- ${display} ${version}` : `- ${display}`);
391
+ }
392
+ if (operatingSystem !== void 0 && operatingSystem.length > 0) platformItems.push(`- Supported platforms: ${operatingSystem.join(", ")}`);
393
+ const peerItems = [];
394
+ if (peerDependencies !== void 0) for (const { name, optional, version } of peerDependencies) {
395
+ const npmUrl = `https://www.npmjs.com/package/${name}`;
396
+ const optionalSuffix = optional ? " _(optional)_" : "";
397
+ peerItems.push(`- [${name}](${npmUrl}) ${version}${optionalSuffix}`);
398
+ }
399
+ const hasPlatform = platformItems.length > 0;
400
+ const hasPeers = peerItems.length > 0;
401
+ if (!hasPlatform && !hasPeers) return "";
402
+ const sections = ["## Dependencies"];
403
+ if (hasPlatform && hasPeers) sections.push("", "### Platform", "", ...platformItems, "", "### Peer Dependencies", "", ...peerItems);
404
+ else if (hasPlatform) sections.push("", ...platformItems);
405
+ else sections.push("", ...peerItems);
406
+ return sections.join("\n");
291
407
  } } };
292
408
  //#endregion
293
409
  //#region src/lib/readme/rules/description.ts
@@ -302,10 +418,10 @@ var description_default = { description: { async content() {
302
418
  //#endregion
303
419
  //#region src/lib/readme/rules/license.ts
304
420
  var license_default = { license: { async content() {
305
- const { author, license, licenseFilePath } = await getReadmeMetadata();
421
+ const { author, authorUrl, license, licenseFilePath } = await getReadmeMetadata();
306
422
  if (author === void 0) throw new Error("Could not find author name in project");
307
423
  if (license === void 0 || licenseFilePath === void 0) throw new Error("Could not find license for project");
308
- return `## License\n[${license}](${licenseFilePath}) © ${author}`;
424
+ return `## License\n[${license}](${licenseFilePath}) © ${authorUrl === void 0 ? author : `[${author}](${authorUrl})`}`;
309
425
  } } };
310
426
  //#endregion
311
427
  //#region src/lib/readme/rules/footer.ts
@@ -349,11 +465,30 @@ var header_default = { header: {
349
465
  order: 2
350
466
  } };
351
467
  //#endregion
468
+ //#region src/lib/readme/rules/install.ts
469
+ var install_default = { install: { async content() {
470
+ const { bin, engines, isPublicNpmPackage, name, runtimePlatform, usesPnpm } = await getReadmeMetadata();
471
+ if (name === void 0) throw new Error("Could not find project name");
472
+ const lines = ["## Install"];
473
+ if (isPublicNpmPackage || engines?.node !== void 0 || runtimePlatform?.includes("node")) {
474
+ const pmAdd = usesPnpm ? "pnpm add" : "npm install";
475
+ const pmx = usesPnpm ? "pnpx" : "npx";
476
+ lines.push("", "```sh", `${pmAdd} ${name}`, "```");
477
+ if (bin !== void 0 && bin.length > 0) lines.push("", "Or run it directly:", "", "```sh", `${pmx} ${name}`, "```");
478
+ } else if (runtimePlatform?.some((p) => p.startsWith("python"))) lines.push("", "```sh", `pip install ${name}`, "```");
479
+ else if (runtimePlatform?.some((p) => p.startsWith("rust"))) lines.push("", "```sh", `cargo install ${name}`, "```");
480
+ else if (runtimePlatform?.some((p) => p.startsWith("go"))) lines.push("", "```sh", `go install ${name}@latest`, "```");
481
+ else if (runtimePlatform?.some((p) => p.startsWith("ruby"))) lines.push("", "```sh", `gem install ${name}`, "```");
482
+ else throw new Error("Could not determine project ecosystem for install instructions");
483
+ return lines.join("\n");
484
+ } } };
485
+ //#endregion
352
486
  //#region src/lib/readme/rules/utilities/size/size-report.ts
353
487
  const brotliCompressAsync = promisify(brotliCompress);
354
488
  const gzipCompressAsync = promisify(gzip);
355
489
  /**
356
490
  * Creates a SizeInfo object with formatted values
491
+ *
357
492
  * @param bytes - Size in bytes
358
493
  * @param originalSize - Original file size for percentage calculation
359
494
  */
@@ -368,9 +503,11 @@ function createSizeInfo(bytes, originalSize) {
368
503
  }
369
504
  /**
370
505
  * Analyzes a file's size and its compressed sizes using Brotli and Gzip
506
+ *
371
507
  * @param filePath - Path to the file to analyze
508
+ *
372
509
  * @returns Promise containing detailed size report
373
- * @throws {Error} if file cannot be read or compressed
510
+ * @throws {Error} If file cannot be read or compressed
374
511
  */
375
512
  async function createSizeReport(filePath) {
376
513
  try {
@@ -501,9 +638,11 @@ var rules_default = {
501
638
  ...banner_default,
502
639
  ...code_default,
503
640
  ...contributing_default,
641
+ ...dependencies_default,
504
642
  ...description_default,
505
643
  ...footer_default,
506
644
  ...header_default,
645
+ ...install_default,
507
646
  ...license_default,
508
647
  ...short_description_default,
509
648
  ...size_default,
@@ -528,9 +667,9 @@ function getAdditionalConfigExplorer() {
528
667
  return _additionalConfigExplorer;
529
668
  }
530
669
  /**
531
- * Load and validate mdat configuration.
532
- * Uses cosmiconfig to search in the usual places.
533
- * Merge precedence: Base Defaults < Defaults < Searched Config < Additional Config
670
+ * Load and validate mdat configuration. Uses cosmiconfig to search in the usual
671
+ * places. Merge precedence: Base Defaults < Defaults < Searched Config <
672
+ * Additional Config
534
673
  */
535
674
  async function loadConfig(options) {
536
675
  const { additionalConfig, defaults = rules_default, searchFrom } = options ?? {};
@@ -581,8 +720,7 @@ function validateConfig(value) {
581
720
  log.warn(`Config has the wrong shape. Ignoring and using default configuration:\n${JSON.stringify(value, void 0, 2)}`);
582
721
  }
583
722
  /**
584
- * Convenience function for merging configs.
585
- * Rightmost config takes precedence.
723
+ * Convenience function for merging configs. Rightmost config takes precedence.
586
724
  */
587
725
  function mergeConfig(a, b) {
588
726
  return deepMergeDefined(a, b);
@@ -598,8 +736,8 @@ function defineConfig(config) {
598
736
  let cachedPrettier;
599
737
  const configCache = /* @__PURE__ */ new Map();
600
738
  /**
601
- * Format a markdown string with Prettier, using config discovered from the file path.
602
- * Requires `prettier` to be installed as a peer dependency.
739
+ * Format a markdown string with Prettier, using config discovered from the file
740
+ * path. Requires `prettier` to be installed as a peer dependency.
603
741
  */
604
742
  async function formatWithPrettier(content, filePath) {
605
743
  if (cachedPrettier === void 0) try {
@@ -775,46 +913,28 @@ function getStripProcessor(_config, ambientRemarkConfig) {
775
913
  });
776
914
  }
777
915
  //#endregion
778
- //#region src/lib/readme/templates/mdat-readme-compound.md?raw
779
- var mdat_readme_compound_default = "<!-- header -->\n\n<!-- table-of-contents -->\n\n## Overview\n\n## Getting started\n\n### Dependencies\n\n### Installation\n\n## Usage\n\n### Library\n\n#### API\n\n#### Examples\n\n### CLI\n\n#### Commands\n\n#### Examples\n\n## Background\n\n### Motivation\n\n### Implementation notes\n\n### Similar projects\n\n## The future\n\n## Maintainers\n\n_List maintainer(s) for a repository, along with one way of contacting them (e.g. GitHub link or email)._\n\n## Acknowledgments\n\n_State anyone or anything that significantly helped with the development of your project. State public contact hyper-links if applicable._\n\n<!-- footer -->\n";
780
- //#endregion
781
- //#region src/lib/readme/templates/mdat-readme.md?raw
782
- var mdat_readme_default = "<!-- title -->\n\n<!-- banner -->\n\n<!-- badges -->\n\n<!-- short-description -->\n\n<!-- table-of-contents -->\n\n## Overview\n\n## Getting started\n\n### Dependencies\n\n### Installation\n\n## Usage\n\n### Library\n\n#### API\n\n#### Examples\n\n### CLI\n\n#### Commands\n\n#### Examples\n\n## Background\n\n### Motivation\n\n### Implementation notes\n\n### Similar projects\n\n## The future\n\n## Maintainers\n\n_List maintainer(s) for a repository, along with one way of contacting them (e.g. GitHub link or email)._\n\n## Acknowledgments\n\n_State anyone or anything that significantly helped with the development of your project. State public contact hyper-links if applicable._\n\n<!-- contributing -->\n\n<!-- license -->\n";
783
- //#endregion
784
- //#region src/lib/readme/templates/standard-readme-basic-compound.md?raw
785
- var standard_readme_basic_compound_default = "<!-- header -->\n\n## Install\n\n```sh\n# Code block illustrating how to install.\n```\n\n## Usage\n\n```ts\n// Code block illustrating common usage.\n// Consider using the <!-- code({ src: \"path/to/example.ts\" }) --> comment as well.\n```\n\n<!-- footer -->\n";
786
- //#endregion
787
- //#region src/lib/readme/templates/standard-readme-basic.md?raw
788
- var standard_readme_basic_default = "<!-- title -->\n\n<!-- short-description -->\n\n<!-- table-of-contents -->\n\n## Install\n\n```sh\n# Code block illustrating how to install.\n```\n\n## Usage\n\n```ts\n// Code block illustrating common usage.\n// Consider using the <!-- code({ src: \"path/to/example.ts\" })--> comment as well.\n```\n\n<!-- contributing -->\n\n<!-- license -->\n";
789
- //#endregion
790
- //#region src/lib/readme/templates/standard-readme-full-compound.md?raw
791
- var standard_readme_full_compound_default = "<!-- header -->\n\n_Long description goes here._\n\n_No heading. Cover the main reasons for building the repository. This should describe your module in broad terms, generally in just a few paragraphs; more detail of the module's routines or methods, lengthy code examples, or other in-depth material should be given in subsequent sections._\n\n<!-- table-of-contents -->\n\n## Background\n\n_Cover motivation. Cover abstract dependencies. Cover intellectual provenance: A See Also section is also fitting._\n\n### See also\n\n## Install\n\n```sh\n# Code block illustrating how to install.\n```\n\n### Dependencies\n\n_Required if there are unusual dependencies or dependencies that must be manually installed._\n\n## Usage\n\n```ts\n// Code block illustrating common usage.\n// Consider using the <!-- code({ src: \"path/to/example.ts\" })--> comment as well.\n```\n\n```ts\n// If importable, code block indicating both import functionality and usage.\n```\n\n### CLI\n\n```ts\n// If CLI compatible, code block indicating common usage.\n```\n\n## _Extra Sections (Rename)_\n\n## Security\n\n## API\n\n_Describe exported functions and objects._\n\n- _Describe signatures, return types, callbacks, and events._\n- _Cover types covered where not obvious._\n- _Describe caveats._\n- _If using an external API generator (like go-doc, js-doc, or so on), point to an external API.md file. This can be the only item in the section, if present._\n\n## Maintainers\n\n_List maintainer(s) for a repository, along with one way of contacting them (e.g. GitHub link or email)._\n\n## Thanks\n\n_State anyone or anything that significantly helped with the development of your project. State public contact hyper-links if applicable._\n\n<!-- footer -->\n";
792
- //#endregion
793
- //#region src/lib/readme/templates/standard-readme-full.md?raw
794
- var standard_readme_full_default = "<!-- title -->\n\n<!-- banner -->\n\n<!-- badges -->\n\n<!-- short-description -->\n\n_Long description goes here._\n\n_No heading. Cover the main reasons for building the repository. This should describe your module in broad terms, generally in just a few paragraphs; more detail of the module's routines or methods, lengthy code examples, or other in-depth material should be given in subsequent sections._\n\n<!-- table-of-contents -->\n\n## Background\n\n_Cover motivation. Cover abstract dependencies. Cover intellectual provenance: A See Also section is also fitting._\n\n### See also\n\n## Install\n\n```sh\n# Code block illustrating how to install.\n```\n\n### Dependencies\n\n_Required if there are unusual dependencies or dependencies that must be manually installed._\n\n## Usage\n\n```ts\n// Code block illustrating common usage.\n// Consider using the <!-- code({ src: \"path/to/example.ts\" })--> comment as well.\n```\n\n```ts\n// If importable, code block indicating both import functionality and usage.\n```\n\n### CLI\n\n```ts\n// If CLI compatible, code block indicating common usage.\n```\n\n## _Extra Sections (Rename)_\n\n## Security\n\n## API\n\n_Describe exported functions and objects._\n\n- _Describe signatures, return types, callbacks, and events._\n- _Cover types covered where not obvious._\n- _Describe caveats._\n- _If using an external API generator (like go-doc, js-doc, or so on), point to an external API.md file. This can be the only item in the section, if present._\n\n## Maintainers\n\n_List maintainer(s) for a repository, along with one way of contacting them (e.g. GitHub link or email)._\n\n## Thanks\n\n_State anyone or anything that significantly helped with the development of your project. State public contact hyper-links if applicable._\n\n<!-- contributing -->\n\n<!-- license -->\n";
795
- //#endregion
796
916
  //#region src/lib/readme/templates/index.ts
797
917
  var templates_default = {
798
918
  "MDAT Readme": {
799
919
  content: {
800
- compound: mdat_readme_compound_default,
801
- explicit: mdat_readme_default
920
+ compound: "<!-- header -->\n\n<!-- table-of-contents -->\n\n## Overview\n\n## Getting started\n\n### Dependencies\n\n### Installation\n\n## Usage\n\n### Library\n\n#### API\n\n#### Examples\n\n### CLI\n\n#### Commands\n\n#### Examples\n\n## Background\n\n### Motivation\n\n### Implementation notes\n\n### Similar projects\n\n## The future\n\n## Maintainers\n\n_List maintainer(s) for a repository, along with one way of contacting them (e.g. GitHub link or email)._\n\n## Acknowledgments\n\n_State anyone or anything that significantly helped with the development of your project. State public contact hyper-links if applicable._\n\n<!-- footer -->\n",
921
+ explicit: "<!-- title -->\n\n<!-- banner -->\n\n<!-- badges -->\n\n<!-- short-description -->\n\n<!-- table-of-contents -->\n\n## Overview\n\n## Getting started\n\n### Dependencies\n\n### Installation\n\n## Usage\n\n### Library\n\n#### API\n\n#### Examples\n\n### CLI\n\n#### Commands\n\n#### Examples\n\n## Background\n\n### Motivation\n\n### Implementation notes\n\n### Similar projects\n\n## The future\n\n## Maintainers\n\n_List maintainer(s) for a repository, along with one way of contacting them (e.g. GitHub link or email)._\n\n## Acknowledgments\n\n_State anyone or anything that significantly helped with the development of your project. State public contact hyper-links if applicable._\n\n<!-- contributing -->\n\n<!-- license -->\n"
802
922
  },
803
923
  description: "The house style. An expansive starting point. Prune to your context and taste.",
804
924
  exampleLink: "https://github.com/kitschpatrol/mdat/blob/main/readme.md"
805
925
  },
806
926
  "Standard Readme Basic": {
807
927
  content: {
808
- compound: standard_readme_basic_compound_default,
809
- explicit: standard_readme_basic_default
928
+ compound: "<!-- header -->\n\n## Install\n\n```sh\n# Code block illustrating how to install.\n```\n\n## Usage\n\n```ts\n// Code block illustrating common usage.\n// Consider using the <!-- code({ src: \"path/to/example.ts\" }) --> comment as well.\n```\n\n<!-- footer -->\n",
929
+ explicit: "<!-- title -->\n\n<!-- short-description -->\n\n<!-- table-of-contents -->\n\n## Install\n\n```sh\n# Code block illustrating how to install.\n```\n\n## Usage\n\n```ts\n// Code block illustrating common usage.\n// Consider using the <!-- code({ src: \"path/to/example.ts\" })--> comment as well.\n```\n\n<!-- contributing -->\n\n<!-- license -->\n"
810
930
  },
811
931
  description: "Includes only the \"required\" sections from the Standard Readme specification.",
812
932
  exampleLink: "https://github.com/RichardLitt/standard-readme/blob/main/example-readmes/minimal-readme.md"
813
933
  },
814
934
  "Standard Readme Full": {
815
935
  content: {
816
- compound: standard_readme_full_compound_default,
817
- explicit: standard_readme_full_default
936
+ compound: "<!-- header -->\n\n_Long description goes here._\n\n_No heading. Cover the main reasons for building the repository. This should describe your module in broad terms, generally in just a few paragraphs; more detail of the module's routines or methods, lengthy code examples, or other in-depth material should be given in subsequent sections._\n\n<!-- table-of-contents -->\n\n## Background\n\n_Cover motivation. Cover abstract dependencies. Cover intellectual provenance: A See Also section is also fitting._\n\n### See also\n\n## Install\n\n```sh\n# Code block illustrating how to install.\n```\n\n### Dependencies\n\n_Required if there are unusual dependencies or dependencies that must be manually installed._\n\n## Usage\n\n```ts\n// Code block illustrating common usage.\n// Consider using the <!-- code({ src: \"path/to/example.ts\" })--> comment as well.\n```\n\n```ts\n// If importable, code block indicating both import functionality and usage.\n```\n\n### CLI\n\n```ts\n// If CLI compatible, code block indicating common usage.\n```\n\n## _Extra Sections (Rename)_\n\n## Security\n\n## API\n\n_Describe exported functions and objects._\n\n- _Describe signatures, return types, callbacks, and events._\n- _Cover types covered where not obvious._\n- _Describe caveats._\n- _If using an external API generator (like go-doc, js-doc, or so on), point to an external API.md file. This can be the only item in the section, if present._\n\n## Maintainers\n\n_List maintainer(s) for a repository, along with one way of contacting them (e.g. GitHub link or email)._\n\n## Thanks\n\n_State anyone or anything that significantly helped with the development of your project. State public contact hyper-links if applicable._\n\n<!-- footer -->\n",
937
+ explicit: "<!-- title -->\n\n<!-- banner -->\n\n<!-- badges -->\n\n<!-- short-description -->\n\n_Long description goes here._\n\n_No heading. Cover the main reasons for building the repository. This should describe your module in broad terms, generally in just a few paragraphs; more detail of the module's routines or methods, lengthy code examples, or other in-depth material should be given in subsequent sections._\n\n<!-- table-of-contents -->\n\n## Background\n\n_Cover motivation. Cover abstract dependencies. Cover intellectual provenance: A See Also section is also fitting._\n\n### See also\n\n## Install\n\n```sh\n# Code block illustrating how to install.\n```\n\n### Dependencies\n\n_Required if there are unusual dependencies or dependencies that must be manually installed._\n\n## Usage\n\n```ts\n// Code block illustrating common usage.\n// Consider using the <!-- code({ src: \"path/to/example.ts\" })--> comment as well.\n```\n\n```ts\n// If importable, code block indicating both import functionality and usage.\n```\n\n### CLI\n\n```ts\n// If CLI compatible, code block indicating common usage.\n```\n\n## _Extra Sections (Rename)_\n\n## Security\n\n## API\n\n_Describe exported functions and objects._\n\n- _Describe signatures, return types, callbacks, and events._\n- _Cover types covered where not obvious._\n- _Describe caveats._\n- _If using an external API generator (like go-doc, js-doc, or so on), point to an external API.md file. This can be the only item in the section, if present._\n\n## Maintainers\n\n_List maintainer(s) for a repository, along with one way of contacting them (e.g. GitHub link or email)._\n\n## Thanks\n\n_State anyone or anything that significantly helped with the development of your project. State public contact hyper-links if applicable._\n\n<!-- contributing -->\n\n<!-- license -->\n"
818
938
  },
819
939
  description: "Includes all sections from the Standard Readme specification.",
820
940
  exampleLink: "https://github.com/RichardLitt/standard-readme/blob/main/example-readmes/maximal-readme.md"
@@ -824,6 +944,7 @@ var templates_default = {
824
944
  //#region src/lib/readme/create.ts
825
945
  /**
826
946
  * Creates a new readme file interactively.
947
+ *
827
948
  * @returns Path to the created readme file.
828
949
  */
829
950
  async function createReadmeInteractive() {
@@ -864,6 +985,7 @@ async function createReadmeInteractive() {
864
985
  }
865
986
  /**
866
987
  * Creates a new readme file with the given options.
988
+ *
867
989
  * @returns Path to the created readme file.
868
990
  */
869
991
  async function createReadme(options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mdat",
3
- "version": "2.2.1",
3
+ "version": "2.3.1",
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",
@@ -45,12 +45,12 @@
45
45
  "dependencies": {
46
46
  "@clack/prompts": "^1.2.0",
47
47
  "cosmiconfig": "^9.0.1",
48
- "cosmiconfig-typescript-loader": "^6.2.0",
48
+ "cosmiconfig-typescript-loader": "^6.3.0",
49
49
  "deepmerge-ts": "^7.1.5",
50
50
  "globby": "^16.2.0",
51
51
  "lognow": "^0.6.1",
52
52
  "mdast-util-toc": "^7.1.0",
53
- "metascope": "^0.6.3",
53
+ "metascope": "^0.7.0",
54
54
  "path-type": "^6.0.0",
55
55
  "picocolors": "^1.1.1",
56
56
  "plur": "^6.0.0",
@@ -58,9 +58,9 @@
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.0",
61
+ "remark-mdat": "^2.2.1",
62
62
  "to-vfile": "^8.0.0",
63
- "type-fest": "^5.5.0",
63
+ "type-fest": "^5.6.0",
64
64
  "unified-engine": "^11.2.2",
65
65
  "untildify": "^6.0.0",
66
66
  "vfile": "^6.0.3",
@@ -69,7 +69,7 @@
69
69
  },
70
70
  "devDependencies": {
71
71
  "@arethetypeswrong/core": "^0.18.2",
72
- "@kitschpatrol/shared-config": "^7.1.0",
72
+ "@kitschpatrol/shared-config": "^7.5.2",
73
73
  "@types/mdast": "^4.0.4",
74
74
  "@types/node": "~22.17.2",
75
75
  "@types/unist": "^3.0.3",
@@ -77,16 +77,17 @@
77
77
  "@vitest/coverage-v8": "4.1.2",
78
78
  "bumpp": "^11.0.1",
79
79
  "execa": "^9.6.1",
80
- "mdat-plugin-cli-help": "^2.1.1",
80
+ "mdat-plugin-cli-help": "^3.0.0",
81
81
  "mdat-plugin-example": "^2.0.0",
82
- "mdat-plugin-tldraw": "^2.0.1",
83
- "nanoid": "^5.1.7",
84
- "prettier": "^3.8.1",
82
+ "mdat-plugin-tldraw": "^2.0.2",
83
+ "nanoid": "^5.1.9",
84
+ "prettier": "^3.8.3",
85
85
  "publint": "^0.3.18",
86
- "tsdown": "^0.21.7",
87
- "typescript": "~5.9.3",
86
+ "shx": "^0.4.0",
87
+ "tsdown": "^0.21.10",
88
+ "typescript": "~6.0.3",
88
89
  "unplugin-raw": "^0.7.0",
89
- "vitest": "^4.1.3"
90
+ "vitest": "^4.1.5"
90
91
  },
91
92
  "peerDependencies": {
92
93
  "prettier": "^3.0.0"
@@ -109,11 +110,11 @@
109
110
  "bench": "vitest bench --run --compare test/benchmarks/baseline.json",
110
111
  "bench:baseline": "vitest bench --run --outputJson test/benchmarks/baseline.json",
111
112
  "build": "tsdown",
112
- "clean": "git rm -f pnpm-lock.yaml ; git clean -fdX",
113
+ "clean": "shx rm -f pnpm-lock.yaml && git clean -fdX -e !.claude/",
113
114
  "coverage": "vitest --coverage --run",
114
115
  "fix": "ksc fix",
115
116
  "lint": "ksc lint",
116
- "release": "bumpp --commit 'Release: %s' && pnpm run build && NPM_AUTH_TOKEN=$(op read 'op://Personal/npm/token') && pnpm publish",
117
+ "release": "bumpp --commit 'Release: %s' && pnpm build && NPM_AUTH_TOKEN=$(op read 'op://Personal/npm/token') && pnpm publish",
117
118
  "test": "vitest run"
118
119
  }
119
120
  }
package/readme.md CHANGED
@@ -572,6 +572,14 @@ See the [Examples section](https://github.com/kitschpatrol/remark-mdat#examples)
572
572
 
573
573
  The project description. Also aliased as `<!-- short-description -->` for [standard-readme](https://github.com/RichardLitt/standard-readme/blob/main/spec.md) compatibility.
574
574
 
575
+ - ##### `<!-- install -->`
576
+
577
+ Ecosystem-aware install instructions derived from project metadata. Emits `pnpm add` / `npm install` for Node packages (plus a `pnpx` / `npx` hint when the project exposes a binary), and falls back to `pip`, `cargo`, `gem`, or `go install` for Python, Rust, Ruby, and Go projects.
578
+
579
+ - ##### `<!-- dependencies -->`
580
+
581
+ Documents platform requirements and peer dependencies. Lists runtime platforms (Node, Python, Rust, Go, Ruby, etc.) with version constraints from `engines` or equivalent metadata, supported operating systems, and peer dependencies with links to npm.
582
+
575
583
  - ##### `<!-- table-of-contents -->`
576
584
 
577
585
  Auto-generated via [mdast-util-toc](https://github.com/syntax-tree/mdast-util-toc). Also aliased as `<!-- toc -->`.
@@ -600,7 +608,7 @@ See the [Examples section](https://github.com/kitschpatrol/remark-mdat#examples)
600
608
 
601
609
  | File | Original | Gzip | Brotli |
602
610
  | ----------- | -------- | ----- | ------ |
603
- | .gitignore | 305 B | 245 B | 216 B |
611
+ | .gitignore | 318 B | 252 B | 237 B |
604
612
  | license.txt | 1 kB | 659 B | 468 B |
605
613
 
606
614
  <!-- /size-table -->
@@ -794,13 +802,17 @@ There's quite a bit of prior art and similar explorations of this problem space:
794
802
 
795
803
  - VitePress' [Markdown file inclusion](https://vitepress.dev/guide/markdown#markdown-file-inclusion)
796
804
 
805
+ - Hiroki Osame's [comment-mark](https://github.com/privatenumber/comment-mark)
806
+
807
+ - Hiroki Osame's [mdeval](https://github.com/privatenumber/mdeval)
808
+
797
809
  ### Implementation notes
798
810
 
799
811
  This project was split from a monorepo containing both `mdat` and `remark-mdat` into separate repos in July 2024.
800
812
 
801
813
  ## Maintainers
802
814
 
803
- [@kitschpatrol](https://github.com/kitschpatrol)
815
+ [kitschpatrol](https://github.com/kitschpatrol)
804
816
 
805
817
  ## Acknowledgments
806
818
 
@@ -812,10 +824,14 @@ This project was split from a monorepo containing both `mdat` and `remark-mdat`
812
824
 
813
825
  ## Contributing
814
826
 
815
- [Issues](https://github.com/kitschpatrol/mdat/issues) and pull requests are welcome.
827
+ [Issues](https://github.com/kitschpatrol/mdat/issues) are welcome and appreciated.
828
+
829
+ Please open an issue to discuss changes before submitting a pull request. Unsolicited PRs (especially AI-generated ones) are unlikely to be merged.
830
+
831
+ This repository uses [@kitschpatrol/shared-config](https://github.com/kitschpatrol/shared-config) (via its `ksc` CLI) for linting and formatting, plus [MDAT](https://github.com/kitschpatrol/mdat) for readme placeholder expansion.
816
832
 
817
833
  ## License
818
834
 
819
- [MIT](license.txt) © Eric Mika
835
+ [MIT](license.txt) © [Eric Mika](https://ericmika.com)
820
836
 
821
837
  <!-- /footer -->
package/dist/.DS_Store DELETED
Binary file