mdat 3.4.1 → 3.6.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 +1 -1
- package/dist/lib/index.d.ts +6 -0
- package/dist/lib/index.js +1 -1
- package/dist/{lib-CG4EOuoa.js → lib-CqQWGRqt.js} +401 -74
- package/package.json +12 -10
- package/readme.md +93 -21
- package/skills/mdat/SKILL.md +140 -0
- package/skills/mdat-plugin-authoring/SKILL.md +177 -0
|
@@ -17,6 +17,7 @@ import { isFile, isFileSync } from "path-type";
|
|
|
17
17
|
import { promisify } from "node:util";
|
|
18
18
|
import { brotliCompress, gzip } from "node:zlib";
|
|
19
19
|
import prettyBytes from "pretty-bytes";
|
|
20
|
+
import { matter } from "gray-matter-es";
|
|
20
21
|
import { toc } from "mdast-util-toc";
|
|
21
22
|
import { VFile } from "vfile";
|
|
22
23
|
import { Configuration } from "unified-engine";
|
|
@@ -25,14 +26,13 @@ import { confirm, group, intro, note, outro, select } from "@clack/prompts";
|
|
|
25
26
|
|
|
26
27
|
//#region package.json
|
|
27
28
|
var name = "mdat";
|
|
28
|
-
var version = "3.
|
|
29
|
+
var version = "3.6.0";
|
|
29
30
|
|
|
30
31
|
//#endregion
|
|
31
32
|
//#region src/lib/deep-merge-defined.ts
|
|
32
33
|
const deepmerge = deepmergeCustom({ mergeArrays: false });
|
|
33
34
|
function stripUndefinedDeep(object) {
|
|
34
|
-
|
|
35
|
-
return Object.entries(object).map(([k, v]) => [k, v !== null && typeof v === "object" ? stripUndefinedDeep(v) : v]).reduce((acc, [k, v]) => v === void 0 ? acc : {
|
|
35
|
+
return Array.isArray(object) ? object.map((v) => v !== null && typeof v === "object" ? stripUndefinedDeep(v) : v).filter((v) => v !== void 0) : Object.entries(object).map(([k, v]) => [k, v !== null && typeof v === "object" ? stripUndefinedDeep(v) : v]).reduce((acc, [k, v]) => v === void 0 ? acc : {
|
|
36
36
|
...acc,
|
|
37
37
|
[k]: v
|
|
38
38
|
}, {});
|
|
@@ -192,6 +192,29 @@ function getHomebrewFormula(packageName, scripts) {
|
|
|
192
192
|
}
|
|
193
193
|
}
|
|
194
194
|
/**
|
|
195
|
+
* VS Code Marketplace item identifier for packages that are VS Code extensions,
|
|
196
|
+
* identified by an `engines.vscode` field.
|
|
197
|
+
*
|
|
198
|
+
* @returns E.g. `kitschpatrol.tldraw-preview`, or undefined if the package
|
|
199
|
+
* isn't an extension
|
|
200
|
+
*/
|
|
201
|
+
function getVscodeExtensionId(nodePackage) {
|
|
202
|
+
return typeof nodePackage?.publisher === "string" && nodePackage.engines?.vscode !== void 0 ? `${nodePackage.publisher}.${nodePackage.name}` : void 0;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Obsidian community plugin identifier, from the plugin's manifest.json.
|
|
206
|
+
*/
|
|
207
|
+
function getObsidianPluginId(manifests) {
|
|
208
|
+
return helpers.firstOf(manifests)?.data.id;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Package location within a monorepo, from the package.json
|
|
212
|
+
* `repository.directory` field.
|
|
213
|
+
*/
|
|
214
|
+
function getRepositoryDirectory(nodePackage) {
|
|
215
|
+
return typeof nodePackage?.repository === "object" ? nodePackage.repository.directory : void 0;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
195
218
|
* Reset cached context metadata. Call between tests or when the underlying
|
|
196
219
|
* project files may have changed on disk.
|
|
197
220
|
*
|
|
@@ -201,12 +224,12 @@ function resetContextMetadata() {
|
|
|
201
224
|
metascopeMetadata = void 0;
|
|
202
225
|
}
|
|
203
226
|
const readmeMetadataTemplate = defineTemplate((context) => {
|
|
204
|
-
const { githubActions, gitStats, licenseFile, metascope, nodePackageJson } = context;
|
|
227
|
+
const { githubActions, gitStats, licenseFile, metascope, nodePackageJson, obsidianPluginManifestJson } = context;
|
|
205
228
|
const codemeta = templates.codemetaJson(context, {});
|
|
206
229
|
const nodePackage = helpers.firstOf(nodePackageJson)?.data;
|
|
207
230
|
const licenseFileData = helpers.firstOf(licenseFile);
|
|
208
231
|
const ciActionFilePath = helpers.ensureArray(githubActions).find((entry) => entry.data.name.toLowerCase() === "ci")?.source;
|
|
209
|
-
const
|
|
232
|
+
const repositoryUrl = codemeta.codeRepository?.replace(GIT_PREFIX_REGEX, "").replace(GIT_SUFFIX_REGEX, "").replace(TRAILING_SLASH_REGEX, "");
|
|
210
233
|
const bin = (() => {
|
|
211
234
|
if (nodePackage === void 0) return;
|
|
212
235
|
const binField = nodePackage.bin;
|
|
@@ -256,6 +279,9 @@ const readmeMetadataTemplate = defineTemplate((context) => {
|
|
|
256
279
|
};
|
|
257
280
|
})();
|
|
258
281
|
const homebrewFormula = getHomebrewFormula(nodePackage?.name, nodePackage?.scripts);
|
|
282
|
+
const repositoryDirectory = getRepositoryDirectory(nodePackage);
|
|
283
|
+
const vscodeExtensionId = getVscodeExtensionId(nodePackage);
|
|
284
|
+
const obsidianPluginId = getObsidianPluginId(obsidianPluginManifestJson);
|
|
259
285
|
const firstAuthor = helpers.firstOf(helpers.ensureArray(codemeta.author));
|
|
260
286
|
return {
|
|
261
287
|
author: helpers.firstOf(helpers.mixedStringsToArray(helpers.toBasicNames(codemeta.author))),
|
|
@@ -275,12 +301,15 @@ const readmeMetadataTemplate = defineTemplate((context) => {
|
|
|
275
301
|
licenseFilePath: licenseFileData?.source,
|
|
276
302
|
licenseUrl: licenseFileData?.data.match?.spdxUrl,
|
|
277
303
|
name: codemeta.name,
|
|
304
|
+
obsidianPluginId,
|
|
278
305
|
operatingSystem: codemeta.operatingSystem ?? getSupportedOperatingSystems(nodePackage?.os),
|
|
279
306
|
peerDependencies,
|
|
280
307
|
projectDirectory: metascope?.data.options.path === void 0 ? void 0 : `file://${metascope.data.options.path}`,
|
|
281
|
-
|
|
308
|
+
repositoryDirectory,
|
|
309
|
+
repositoryUrl,
|
|
282
310
|
runtimePlatform: codemeta.runtimePlatform,
|
|
283
|
-
usesGitLfs: helpers.firstOf(gitStats)?.data.hasLfs === true
|
|
311
|
+
usesGitLfs: helpers.firstOf(gitStats)?.data.hasLfs === true,
|
|
312
|
+
vscodeExtensionId
|
|
284
313
|
};
|
|
285
314
|
});
|
|
286
315
|
let readmeMetadata;
|
|
@@ -315,24 +344,159 @@ function resetMetadataCaches() {
|
|
|
315
344
|
|
|
316
345
|
//#endregion
|
|
317
346
|
//#region src/lib/readme/rules/badges.ts
|
|
347
|
+
const HOMEBREW_FORMULA_REGEX = /^(?<owner>[^\/]+)\/(?<tap>[^\/]+)\/(?<formula>[^\/]+)$/v;
|
|
348
|
+
const GITHUB_REPOSITORY_REGEX$1 = /^https:\/\/github\.com\/(?<owner>[^\/]+)\/(?<repo>[^\/]+)$/v;
|
|
349
|
+
const NO_NPM_PACKAGE = "no public npm package was found. Remove \"private\": true from package.json if the package is published, or list packages explicitly, e.g. badges({ npm: [\"package-name\"] })";
|
|
350
|
+
const optionsSchema$2 = z.object({
|
|
351
|
+
bundleSize: z.boolean().optional(),
|
|
352
|
+
ci: z.boolean().optional(),
|
|
353
|
+
custom: z.record(z.string(), z.object({
|
|
354
|
+
image: z.string(),
|
|
355
|
+
link: z.string()
|
|
356
|
+
})).default({}),
|
|
357
|
+
githubRelease: z.boolean().optional(),
|
|
358
|
+
gitLfs: z.boolean().optional(),
|
|
359
|
+
homebrew: z.union([z.boolean(), z.string().regex(HOMEBREW_FORMULA_REGEX, "The badges \"homebrew\" option expects a formula in the form \"owner/tap/formula\"")]).optional(),
|
|
360
|
+
license: z.boolean().optional(),
|
|
361
|
+
npm: z.union([z.boolean(), z.array(z.string()).min(1, "Pass npm: false instead of an empty array to hide the NPM version badge")]).optional(),
|
|
362
|
+
npmDownloads: z.boolean().optional(),
|
|
363
|
+
obsidianDownloads: z.boolean().optional(),
|
|
364
|
+
vscode: z.boolean().optional()
|
|
365
|
+
});
|
|
366
|
+
/**
|
|
367
|
+
* Escape text for a path segment of a shields.io static badge, where `-` and
|
|
368
|
+
* `_` are delimiters and must be doubled to appear literally.
|
|
369
|
+
*/
|
|
370
|
+
function escapeStaticBadgeText(text) {
|
|
371
|
+
return encodeURIComponent(text.replaceAll("-", "--").replaceAll("_", "__"));
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* One badge per npm package, for badges that report on npm packages.
|
|
375
|
+
*/
|
|
376
|
+
function mapNpmPackages(npmPackages, toBadge) {
|
|
377
|
+
return npmPackages.length === 0 ? { unavailable: NO_NPM_PACKAGE } : npmPackages.map((packageName) => toBadge(packageName));
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Owner and name of the project's GitHub repository, or undefined if the
|
|
381
|
+
* repository isn't on GitHub.
|
|
382
|
+
*/
|
|
383
|
+
function getGithubRepository(repositoryUrl = "") {
|
|
384
|
+
const { owner, repo } = GITHUB_REPOSITORY_REGEX$1.exec(repositoryUrl)?.groups ?? {};
|
|
385
|
+
return owner === void 0 || repo === void 0 ? void 0 : {
|
|
386
|
+
owner,
|
|
387
|
+
repo
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Reason for badges that need a GitHub repository.
|
|
392
|
+
*/
|
|
393
|
+
function getNotGithubReason(repositoryUrl) {
|
|
394
|
+
return `${repositoryUrl === void 0 ? "no repository URL was found in the project metadata" : `the repository URL "${repositoryUrl}" is not a GitHub repository`}. Set the "repository" field in package.json to the project's GitHub repository.`;
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Explicit package names are useful for monorepos. Otherwise the package itself
|
|
398
|
+
* is used if it's public, even when the NPM version badge is hidden, so the
|
|
399
|
+
* download and size badges can still appear.
|
|
400
|
+
*/
|
|
401
|
+
function getNpmPackages(option, { isPublicNpmPackage, name }) {
|
|
402
|
+
if (Array.isArray(option)) return option;
|
|
403
|
+
return isPublicNpmPackage && name !== void 0 ? [name] : [];
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Badges in display order. Each appears when its metadata is available unless
|
|
407
|
+
* it's `optIn`, and its option can hide it (`false`) or require it (`true`).
|
|
408
|
+
*/
|
|
409
|
+
const badgeDefinitions = [
|
|
410
|
+
{
|
|
411
|
+
key: "npm",
|
|
412
|
+
render: ({ npmPackages }) => mapNpmPackages(npmPackages, (packageName) => `[](https://www.npmjs.com/package/${packageName})`)
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
key: "license",
|
|
416
|
+
render: ({ license, licenseUrl }) => license === void 0 || licenseUrl === void 0 ? { unavailable: "no license file matching a known SPDX license was found" } : [`[}-yellow.svg)](${licenseUrl})`]
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
key: "ci",
|
|
420
|
+
render({ ciActionFileName, repositoryUrl }) {
|
|
421
|
+
if (ciActionFileName === void 0) return { unavailable: "no GitHub Actions workflow named \"CI\" was found in .github/workflows" };
|
|
422
|
+
if (repositoryUrl === void 0) return { unavailable: "no repository URL was found in the project metadata" };
|
|
423
|
+
return [`[](${repositoryUrl}/actions/workflows/${ciActionFileName})`];
|
|
424
|
+
}
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
key: "gitLfs",
|
|
428
|
+
render: ({ usesGitLfs }) => usesGitLfs ? [""] : { unavailable: "the repository does not track any files with Git LFS" }
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
key: "vscode",
|
|
432
|
+
render({ repositoryDirectory, repositoryUrl, vscodeExtensionId }) {
|
|
433
|
+
if (vscodeExtensionId === void 0) return { unavailable: "package.json is missing a \"publisher\" or \"engines.vscode\" field" };
|
|
434
|
+
const repository = getGithubRepository(repositoryUrl);
|
|
435
|
+
if (repository === void 0) return { unavailable: `the extension version is read from package.json on GitHub, but ${getNotGithubReason(repositoryUrl)}` };
|
|
436
|
+
const packageJsonPath = [repositoryDirectory, "package.json"].filter((segment) => segment !== void 0).join("/");
|
|
437
|
+
const packageJsonUrl = `https://raw.githubusercontent.com/${repository.owner}/${repository.repo}/HEAD/${packageJsonPath}`;
|
|
438
|
+
return [`[}&query=version&label=VS%20Code%20Marketplace)](https://marketplace.visualstudio.com/items?itemName=${vscodeExtensionId})`];
|
|
439
|
+
}
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
key: "homebrew",
|
|
443
|
+
render({ homebrewFormula, options }) {
|
|
444
|
+
const formulaPath = typeof options.homebrew === "string" ? options.homebrew : homebrewFormula;
|
|
445
|
+
if (formulaPath === void 0) return { unavailable: "no \"brewpub --tap <owner>/<tap>\" invocation was found in the package.json scripts. Pass the formula explicitly instead, e.g. badges({ homebrew: \"owner/tap/formula\" })" };
|
|
446
|
+
const formulaParts = HOMEBREW_FORMULA_REGEX.exec(formulaPath)?.groups;
|
|
447
|
+
if (formulaParts?.owner === void 0 || formulaParts.tap === void 0 || formulaParts.formula === void 0) return { unavailable: `the detected formula "${formulaPath}" is not in the form "owner/tap/formula"` };
|
|
448
|
+
const { formula, owner, tap } = formulaParts;
|
|
449
|
+
return [`[}-FBB040?logo=homebrew&logoColor=white)](https://github.com/${owner}/homebrew-${tap}/blob/HEAD/Formula/${formula}.rb)`];
|
|
450
|
+
}
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
key: "githubRelease",
|
|
454
|
+
optIn: true,
|
|
455
|
+
render({ repositoryUrl }) {
|
|
456
|
+
const repository = getGithubRepository(repositoryUrl);
|
|
457
|
+
if (repository === void 0) return { unavailable: getNotGithubReason(repositoryUrl) };
|
|
458
|
+
const { owner, repo } = repository;
|
|
459
|
+
return [`[](https://github.com/${owner}/${repo}/releases/latest)`];
|
|
460
|
+
}
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
key: "npmDownloads",
|
|
464
|
+
optIn: true,
|
|
465
|
+
render: ({ npmPackages }) => mapNpmPackages(npmPackages, (packageName) => `[](https://www.npmjs.com/package/${packageName})`)
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
key: "obsidianDownloads",
|
|
469
|
+
optIn: true,
|
|
470
|
+
render({ obsidianPluginId }) {
|
|
471
|
+
if (obsidianPluginId === void 0) return { unavailable: "no Obsidian plugin manifest.json was found" };
|
|
472
|
+
const statsUrl = "https://raw.githubusercontent.com/obsidianmd/obsidian-releases/master/community-plugin-stats.json";
|
|
473
|
+
const query = `$["${obsidianPluginId}"].downloads`;
|
|
474
|
+
return [`[}&url=${encodeURIComponent(statsUrl)})](https://community.obsidian.md/plugins/${obsidianPluginId})`];
|
|
475
|
+
}
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
key: "bundleSize",
|
|
479
|
+
optIn: true,
|
|
480
|
+
render: ({ npmPackages }) => mapNpmPackages(npmPackages, (packageName) => `[](https://bundlephobia.com/package/${packageName})`)
|
|
481
|
+
}
|
|
482
|
+
];
|
|
318
483
|
var badges_default = { badges: { async content(options) {
|
|
319
|
-
const validOptions =
|
|
320
|
-
custom: z.record(z.string(), z.object({
|
|
321
|
-
image: z.string(),
|
|
322
|
-
link: z.string()
|
|
323
|
-
})).optional(),
|
|
324
|
-
npm: z.array(z.string()).optional()
|
|
325
|
-
}).optional().parse(options);
|
|
484
|
+
const validOptions = optionsSchema$2.parse(options ?? {});
|
|
326
485
|
const metadata = await getReadmeMetadata();
|
|
327
|
-
const
|
|
486
|
+
const context = {
|
|
487
|
+
...metadata,
|
|
488
|
+
npmPackages: getNpmPackages(validOptions.npm, metadata),
|
|
489
|
+
options: validOptions
|
|
490
|
+
};
|
|
328
491
|
const badges = [];
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
492
|
+
for (const { key, optIn = false, render } of badgeDefinitions) {
|
|
493
|
+
const option = validOptions[key];
|
|
494
|
+
if (option === false || optIn && option === void 0) continue;
|
|
495
|
+
const result = render(context);
|
|
496
|
+
if (Array.isArray(result)) badges.push(...result);
|
|
497
|
+
else if (option !== void 0) throw new Error(`The "${key}" badge was requested, but ${result.unavailable}`);
|
|
498
|
+
}
|
|
499
|
+
for (const [badgeName, { image, link }] of Object.entries(validOptions.custom)) badges.push(`[](${link})`);
|
|
336
500
|
return badges.join("\n");
|
|
337
501
|
} } };
|
|
338
502
|
|
|
@@ -417,29 +581,51 @@ var code_default = { code: { async content(options) {
|
|
|
417
581
|
|
|
418
582
|
//#endregion
|
|
419
583
|
//#region src/lib/readme/rules/utilities/heading.ts
|
|
584
|
+
const MAX_HEADING_LEVEL = 6;
|
|
420
585
|
/**
|
|
421
586
|
* Option schema for the Markdown heading level (1-6) at which a rule emits its
|
|
422
|
-
* section heading. Sub-headings, if any, are nested one level deeper
|
|
423
|
-
*
|
|
424
|
-
*
|
|
587
|
+
* section heading. Sub-headings, if any, are nested one level deeper, whether
|
|
588
|
+
* or not the section heading itself is shown. Each rule's default matches its
|
|
589
|
+
* placement in the house readme template.
|
|
590
|
+
*/
|
|
591
|
+
const headingLevelSchema = z.number().int().min(1).max(MAX_HEADING_LEVEL).optional();
|
|
592
|
+
/**
|
|
593
|
+
* Option schema for a rule's section heading. `true` (the default) emits the
|
|
594
|
+
* rule's standard heading, `false` leaves the heading out, and a string
|
|
595
|
+
* replaces the heading text.
|
|
425
596
|
*/
|
|
426
|
-
const
|
|
597
|
+
const headingSchema = z.union([z.boolean(), z.string().trim().min(1)]).optional();
|
|
427
598
|
/**
|
|
428
599
|
* The `#` prefix for a Markdown heading, clamped to the maximum level of 6.
|
|
429
600
|
*/
|
|
430
601
|
function getHeadingPrefix(level) {
|
|
431
|
-
return "#".repeat(Math.min(level,
|
|
602
|
+
return "#".repeat(Math.min(level, MAX_HEADING_LEVEL));
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* The lines of a rule's section heading, including the blank line separating it
|
|
606
|
+
* from the section body, or no lines if the heading is suppressed.
|
|
607
|
+
*
|
|
608
|
+
* @param heading - Value of the rule's `heading` option
|
|
609
|
+
* @param level - Markdown heading level
|
|
610
|
+
* @param defaultText - Heading text used unless `heading` overrides it
|
|
611
|
+
*/
|
|
612
|
+
function getHeadingLines(heading, level, defaultText) {
|
|
613
|
+
if (heading === false) return [];
|
|
614
|
+
const text = typeof heading === "string" ? heading : defaultText;
|
|
615
|
+
return [`${getHeadingPrefix(level)} ${text}`, ""];
|
|
432
616
|
}
|
|
433
617
|
|
|
434
618
|
//#endregion
|
|
435
619
|
//#region src/lib/readme/rules/contributing.ts
|
|
436
620
|
var contributing_default = { contributing: { async content(options) {
|
|
437
|
-
const validOptions = z.object({
|
|
621
|
+
const validOptions = z.object({
|
|
622
|
+
heading: headingSchema,
|
|
623
|
+
headingLevel: headingLevelSchema
|
|
624
|
+
}).optional().parse(options);
|
|
438
625
|
const { issuesUrl } = await getReadmeMetadata();
|
|
439
626
|
if (issuesUrl === void 0) throw new Error("Could not find \"bugs.url\" entry in package.json");
|
|
440
627
|
return [
|
|
441
|
-
|
|
442
|
-
"",
|
|
628
|
+
...getHeadingLines(validOptions?.heading, validOptions?.headingLevel ?? 2, "Contributing"),
|
|
443
629
|
`[Issues](${issuesUrl}) are welcome and appreciated.`,
|
|
444
630
|
"",
|
|
445
631
|
"Please open an issue to discuss changes before submitting a pull request. Unsolicited PRs (especially AI-generated ones) are unlikely to be merged.",
|
|
@@ -529,7 +715,10 @@ function getPlatformItem(key, version) {
|
|
|
529
715
|
return version === void 0 || version.trim() === "" ? `- ${display}` : `- ${display} ${describeVersionRange(version)}`;
|
|
530
716
|
}
|
|
531
717
|
var dependencies_default = { dependencies: { async content(options) {
|
|
532
|
-
const validOptions = z.object({
|
|
718
|
+
const validOptions = z.object({
|
|
719
|
+
heading: headingSchema,
|
|
720
|
+
headingLevel: headingLevelSchema
|
|
721
|
+
}).optional().parse(options);
|
|
533
722
|
const { engines, operatingSystem, peerDependencies, runtimePlatform } = await getReadmeMetadata();
|
|
534
723
|
const items = [];
|
|
535
724
|
const engineEntries = Object.entries(engines ?? {});
|
|
@@ -550,11 +739,7 @@ var dependencies_default = { dependencies: { async content(options) {
|
|
|
550
739
|
items.push(`- [${name}](${npmUrl}) \`${version}\` _(peer dependency)_`);
|
|
551
740
|
}
|
|
552
741
|
if (items.length === 0) return "";
|
|
553
|
-
return [
|
|
554
|
-
`${getHeadingPrefix(validOptions?.headingLevel ?? 3)} Dependencies`,
|
|
555
|
-
"",
|
|
556
|
-
...items
|
|
557
|
-
].join("\n");
|
|
742
|
+
return [...getHeadingLines(validOptions?.heading, validOptions?.headingLevel ?? 3, "Dependencies"), ...items].join("\n");
|
|
558
743
|
} } };
|
|
559
744
|
|
|
560
745
|
//#endregion
|
|
@@ -597,7 +782,10 @@ const TOOL_INFO = {
|
|
|
597
782
|
}
|
|
598
783
|
};
|
|
599
784
|
var development_dependencies_default = { "development-dependencies": { async content(options) {
|
|
600
|
-
const validOptions = z.object({
|
|
785
|
+
const validOptions = z.object({
|
|
786
|
+
heading: headingSchema,
|
|
787
|
+
headingLevel: headingLevelSchema
|
|
788
|
+
}).optional().parse(options);
|
|
601
789
|
const { developmentDependencies } = await getReadmeMetadata();
|
|
602
790
|
if (developmentDependencies === void 0) return "";
|
|
603
791
|
const { packageManagers, runtimes } = developmentDependencies;
|
|
@@ -607,11 +795,7 @@ var development_dependencies_default = { "development-dependencies": { async con
|
|
|
607
795
|
const display = info ? `[${info.display}](${info.url})` : name;
|
|
608
796
|
items.push(version === void 0 || version === "" ? `- ${display}` : `- ${display} ${version}`);
|
|
609
797
|
}
|
|
610
|
-
return [
|
|
611
|
-
`${getHeadingPrefix(validOptions?.headingLevel ?? 3)} Development dependencies`,
|
|
612
|
-
"",
|
|
613
|
-
...items
|
|
614
|
-
].join("\n");
|
|
798
|
+
return [...getHeadingLines(validOptions?.heading, validOptions?.headingLevel ?? 3, "Development dependencies"), ...items].join("\n");
|
|
615
799
|
} } };
|
|
616
800
|
|
|
617
801
|
//#endregion
|
|
@@ -624,12 +808,15 @@ var dev_dependencies_default = { "dev-dependencies": getSoleRule(development_dep
|
|
|
624
808
|
//#endregion
|
|
625
809
|
//#region src/lib/readme/rules/license.ts
|
|
626
810
|
var license_default = { license: { async content(options) {
|
|
627
|
-
const validOptions = z.object({
|
|
811
|
+
const validOptions = z.object({
|
|
812
|
+
heading: headingSchema,
|
|
813
|
+
headingLevel: headingLevelSchema
|
|
814
|
+
}).optional().parse(options);
|
|
628
815
|
const { author, authorUrl, license, licenseFilePath } = await getReadmeMetadata();
|
|
629
816
|
if (author === void 0) throw new Error("Could not find author name in project");
|
|
630
817
|
if (license === void 0 || licenseFilePath === void 0) throw new Error("Could not find license for project");
|
|
631
818
|
const authorDisplay = authorUrl === void 0 ? author : `[${author}](${authorUrl})`;
|
|
632
|
-
return
|
|
819
|
+
return [...getHeadingLines(validOptions?.heading, validOptions?.headingLevel ?? 2, "License"), `[${license}](${licenseFilePath}) © ${authorDisplay}`].join("\n");
|
|
633
820
|
} } };
|
|
634
821
|
|
|
635
822
|
//#endregion
|
|
@@ -683,7 +870,7 @@ const listFormatter = new Intl.ListFormat("en", {
|
|
|
683
870
|
style: "long",
|
|
684
871
|
type: "conjunction"
|
|
685
872
|
});
|
|
686
|
-
function codeBlock(command) {
|
|
873
|
+
function codeBlock$1(command) {
|
|
687
874
|
return [
|
|
688
875
|
"```sh",
|
|
689
876
|
command,
|
|
@@ -698,17 +885,17 @@ function getCliLines(name, homebrewFormula) {
|
|
|
698
885
|
return [
|
|
699
886
|
"Run it once without installing:",
|
|
700
887
|
"",
|
|
701
|
-
...codeBlock(`npx ${name}`),
|
|
888
|
+
...codeBlock$1(`npx ${name}`),
|
|
702
889
|
...homebrewFormula === void 0 ? [] : [
|
|
703
890
|
"",
|
|
704
891
|
"Or install it globally with Homebrew:",
|
|
705
892
|
"",
|
|
706
|
-
...codeBlock(`brew install ${homebrewFormula}`)
|
|
893
|
+
...codeBlock$1(`brew install ${homebrewFormula}`)
|
|
707
894
|
],
|
|
708
895
|
"",
|
|
709
896
|
"Or install it globally with npm:",
|
|
710
897
|
"",
|
|
711
|
-
...codeBlock(`npm install --global ${name}`)
|
|
898
|
+
...codeBlock$1(`npm install --global ${name}`)
|
|
712
899
|
];
|
|
713
900
|
}
|
|
714
901
|
/**
|
|
@@ -720,34 +907,34 @@ function getLibraryLines(name, bin, hasTypes, dev) {
|
|
|
720
907
|
if (bin === void 0 || bin.length === 0) return [
|
|
721
908
|
`${lead}:`,
|
|
722
909
|
"",
|
|
723
|
-
...codeBlock(command)
|
|
910
|
+
...codeBlock$1(command)
|
|
724
911
|
];
|
|
725
912
|
return [
|
|
726
913
|
`${lead} to import the ${hasTypes ? "TypeScript API" : "API"}. This also puts the ${listFormatter.format(bin.map((binName) => `\`${binName}\``))} ${bin.length === 1 ? "CLI" : "CLIs"} on your project's path:`,
|
|
727
914
|
"",
|
|
728
|
-
...codeBlock(command)
|
|
915
|
+
...codeBlock$1(command)
|
|
729
916
|
];
|
|
730
917
|
}
|
|
731
918
|
var install_default = { install: { async content(options) {
|
|
732
919
|
const validOptions = z.object({
|
|
733
920
|
dev: z.boolean().optional(),
|
|
921
|
+
heading: headingSchema,
|
|
734
922
|
headingLevel: headingLevelSchema,
|
|
735
923
|
homebrew: z.union([z.string(), z.literal(false)]).optional()
|
|
736
|
-
}).
|
|
924
|
+
}).parse(options ?? {});
|
|
737
925
|
const { bin, hasTypes, homebrewFormula, isLibrary, isNodePackage, isPublicNpmPackage, name, runtimePlatform } = await getReadmeMetadata();
|
|
738
926
|
if (name === void 0) throw new Error("Could not find project name");
|
|
739
|
-
const headingLevel = validOptions
|
|
740
|
-
const
|
|
927
|
+
const headingLevel = validOptions.headingLevel ?? 3;
|
|
928
|
+
const headingLines = getHeadingLines(validOptions.heading, headingLevel, "Installation");
|
|
741
929
|
const subheading = getHeadingPrefix(headingLevel + 1);
|
|
742
930
|
if (isNodePackage) {
|
|
743
931
|
if (!isPublicNpmPackage) throw new Error("Install instructions require a publishable package, but package.json sets \"private\": true");
|
|
744
|
-
const homebrew = validOptions
|
|
932
|
+
const homebrew = validOptions.homebrew === void 0 ? homebrewFormula : validOptions.homebrew === false ? void 0 : validOptions.homebrew;
|
|
745
933
|
const hasCli = bin !== void 0 && bin.length > 0;
|
|
746
934
|
const cliLines = getCliLines(name, homebrew);
|
|
747
|
-
const libraryLines = getLibraryLines(name, bin, hasTypes, validOptions
|
|
935
|
+
const libraryLines = getLibraryLines(name, bin, hasTypes, validOptions.dev ?? false);
|
|
748
936
|
if (hasCli && isLibrary) return [
|
|
749
|
-
|
|
750
|
-
"",
|
|
937
|
+
...headingLines,
|
|
751
938
|
`There are several ways to install ${name} depending on how you're planning to use it:`,
|
|
752
939
|
"",
|
|
753
940
|
`${subheading} CLI`,
|
|
@@ -758,22 +945,21 @@ var install_default = { install: { async content(options) {
|
|
|
758
945
|
"",
|
|
759
946
|
...libraryLines
|
|
760
947
|
].join("\n");
|
|
761
|
-
return [
|
|
762
|
-
heading,
|
|
763
|
-
"",
|
|
764
|
-
...hasCli ? cliLines : libraryLines
|
|
765
|
-
].join("\n");
|
|
948
|
+
return [...headingLines, ...hasCli ? cliLines : libraryLines].join("\n");
|
|
766
949
|
}
|
|
767
950
|
const platform = (prefix) => runtimePlatform?.some((p) => p.startsWith(prefix));
|
|
768
951
|
const command = platform("python") ? `pip install ${name}` : platform("rust") ? `cargo install ${name}` : platform("go") ? `go install ${name}@latest` : platform("ruby") ? `gem install ${name}` : void 0;
|
|
769
952
|
if (command === void 0) throw new Error("Could not determine project ecosystem for install instructions");
|
|
770
|
-
return [
|
|
771
|
-
heading,
|
|
772
|
-
"",
|
|
773
|
-
...codeBlock(command)
|
|
774
|
-
].join("\n");
|
|
953
|
+
return [...headingLines, ...codeBlock$1(command)].join("\n");
|
|
775
954
|
} } };
|
|
776
955
|
|
|
956
|
+
//#endregion
|
|
957
|
+
//#region src/lib/readme/rules/installation.ts
|
|
958
|
+
/**
|
|
959
|
+
* Simple alias for `install`
|
|
960
|
+
*/
|
|
961
|
+
var installation_default = { installation: getSoleRule(install_default) };
|
|
962
|
+
|
|
777
963
|
//#endregion
|
|
778
964
|
//#region src/lib/readme/rules/utilities/size/size-report.ts
|
|
779
965
|
const brotliCompressAsync = promisify(brotliCompress);
|
|
@@ -899,6 +1085,127 @@ var size_table_default = { "size-table": { async content(options) {
|
|
|
899
1085
|
});
|
|
900
1086
|
} } };
|
|
901
1087
|
|
|
1088
|
+
//#endregion
|
|
1089
|
+
//#region src/lib/readme/rules/skills.ts
|
|
1090
|
+
const SKILLS_DIRECTORY = "skills";
|
|
1091
|
+
const SKILL_FILE_NAME = "SKILL.md";
|
|
1092
|
+
const GITHUB_REPOSITORY_REGEX = /^https:\/\/github\.com\/(?<slug>[^\/]+\/[^\/]+)$/v;
|
|
1093
|
+
const WHITESPACE_REGEX = /\s+/gv;
|
|
1094
|
+
const FIRST_SENTENCE_REGEX = /^.*?(?<!\b(?:e\.g|i\.e|vs))[.!?](?=\s+\p{Lu})/v;
|
|
1095
|
+
const skillFrontmatterSchema = z.object({
|
|
1096
|
+
description: z.string().trim().min(1),
|
|
1097
|
+
name: z.string().trim().min(1)
|
|
1098
|
+
});
|
|
1099
|
+
function codeBlock(command) {
|
|
1100
|
+
return [
|
|
1101
|
+
"```sh",
|
|
1102
|
+
command,
|
|
1103
|
+
"```"
|
|
1104
|
+
];
|
|
1105
|
+
}
|
|
1106
|
+
/**
|
|
1107
|
+
* Find Agent Skills in the project's `skills` directory, where each skill is a
|
|
1108
|
+
* subdirectory containing a `SKILL.md` file with `name` and `description`
|
|
1109
|
+
* frontmatter.
|
|
1110
|
+
*
|
|
1111
|
+
* @throws {Error} If a `SKILL.md` file has missing or invalid frontmatter
|
|
1112
|
+
*/
|
|
1113
|
+
async function findSkills(projectDirectory) {
|
|
1114
|
+
const skillsDirectory = path.join(projectDirectory, SKILLS_DIRECTORY);
|
|
1115
|
+
let entries;
|
|
1116
|
+
try {
|
|
1117
|
+
entries = await fs.readdir(skillsDirectory, { withFileTypes: true });
|
|
1118
|
+
} catch (error) {
|
|
1119
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") return [];
|
|
1120
|
+
throw error;
|
|
1121
|
+
}
|
|
1122
|
+
const skills = [];
|
|
1123
|
+
for (const entry of entries) {
|
|
1124
|
+
if (!entry.isDirectory()) continue;
|
|
1125
|
+
const skillFilePath = path.join(skillsDirectory, entry.name, SKILL_FILE_NAME);
|
|
1126
|
+
let skillFile;
|
|
1127
|
+
try {
|
|
1128
|
+
skillFile = await fs.readFile(skillFilePath, "utf8");
|
|
1129
|
+
} catch (error) {
|
|
1130
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") continue;
|
|
1131
|
+
throw error;
|
|
1132
|
+
}
|
|
1133
|
+
const frontmatter = skillFrontmatterSchema.safeParse(matter(skillFile).data);
|
|
1134
|
+
if (!frontmatter.success) throw new Error(`Skill at "${path.relative(projectDirectory, skillFilePath)}" needs "name" and "description" strings in its frontmatter`);
|
|
1135
|
+
skills.push({
|
|
1136
|
+
...frontmatter.data,
|
|
1137
|
+
filePath: `${SKILLS_DIRECTORY}/${entry.name}/${SKILL_FILE_NAME}`
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
return skills.toSorted((a, b) => a.name.localeCompare(b.name));
|
|
1141
|
+
}
|
|
1142
|
+
/**
|
|
1143
|
+
* The argument identifying a repository to the `skills add` command, which
|
|
1144
|
+
* accepts an `owner/repo` shorthand for GitHub and full URLs for other hosts.
|
|
1145
|
+
*/
|
|
1146
|
+
function getSkillsCliSource(repositoryUrl) {
|
|
1147
|
+
return GITHUB_REPOSITORY_REGEX.exec(repositoryUrl)?.groups?.slug ?? repositoryUrl;
|
|
1148
|
+
}
|
|
1149
|
+
/**
|
|
1150
|
+
* The first sentence of a skill description, which by convention says what the
|
|
1151
|
+
* skill does, leaving out the guidance on when to use it that follows.
|
|
1152
|
+
*/
|
|
1153
|
+
function getFirstSentence(description) {
|
|
1154
|
+
const singleLine = description.replaceAll(WHITESPACE_REGEX, " ");
|
|
1155
|
+
return FIRST_SENTENCE_REGEX.exec(singleLine)?.[0] ?? singleLine;
|
|
1156
|
+
}
|
|
1157
|
+
var skills_default = { skills: { async content(options) {
|
|
1158
|
+
const validOptions = z.object({
|
|
1159
|
+
heading: headingSchema,
|
|
1160
|
+
headingLevel: headingLevelSchema
|
|
1161
|
+
}).optional().parse(options);
|
|
1162
|
+
const skills = await findSkills(process.cwd());
|
|
1163
|
+
if (skills.length === 0) throw new Error(`Could not find any skills, expected at least one "${SKILLS_DIRECTORY}/<name>/${SKILL_FILE_NAME}" file in the project`);
|
|
1164
|
+
const { isPublicNpmPackage, name, repositoryUrl } = await getReadmeMetadata();
|
|
1165
|
+
if (name === void 0) throw new Error("Could not find project name");
|
|
1166
|
+
const headingLevel = validOptions?.headingLevel ?? 2;
|
|
1167
|
+
const skillHeading = getHeadingPrefix(headingLevel + 1);
|
|
1168
|
+
const skillNoun = plur("skill", skills.length);
|
|
1169
|
+
const agentSkillsLink = `${skills.length === 1 ? "an" : skills.length} [${plur("Agent Skill", skills.length)}](https://agentskills.io)`;
|
|
1170
|
+
const skillsCliLink = "Vercel's [skills CLI](https://github.com/vercel-labs/skills)";
|
|
1171
|
+
const headingLines = getHeadingLines(validOptions?.heading, headingLevel, "Agent skills");
|
|
1172
|
+
const skillLines = [`Included ${skillNoun}:`, ...skills.flatMap((skill) => [
|
|
1173
|
+
"",
|
|
1174
|
+
`${skillHeading} Skill: [\`${skill.name}\`](${skill.filePath})`,
|
|
1175
|
+
"",
|
|
1176
|
+
getFirstSentence(skill.description)
|
|
1177
|
+
])];
|
|
1178
|
+
if (!isPublicNpmPackage) {
|
|
1179
|
+
if (repositoryUrl === void 0) throw new Error("Skill install instructions require either a publishable npm package or a repository URL in the project metadata");
|
|
1180
|
+
return [
|
|
1181
|
+
...headingLines,
|
|
1182
|
+
`This project includes ${agentSkillsLink} to help coding agents work with ${name}.`,
|
|
1183
|
+
"",
|
|
1184
|
+
`To install the ${skillNoun}, run ${skillsCliLink}:`,
|
|
1185
|
+
"",
|
|
1186
|
+
...codeBlock(`npx skills add ${getSkillsCliSource(repositoryUrl)}`),
|
|
1187
|
+
"",
|
|
1188
|
+
...skillLines
|
|
1189
|
+
].join("\n");
|
|
1190
|
+
}
|
|
1191
|
+
return [
|
|
1192
|
+
...headingLines,
|
|
1193
|
+
`This project bundles ${agentSkillsLink} in its published package to help coding agents work with ${name}.`,
|
|
1194
|
+
"",
|
|
1195
|
+
`To sync the ${skillNoun} into your project, run ${skillsCliLink} from your project root:`,
|
|
1196
|
+
"",
|
|
1197
|
+
...codeBlock("npx skills experimental_sync"),
|
|
1198
|
+
...repositoryUrl === void 0 ? [] : [
|
|
1199
|
+
"",
|
|
1200
|
+
"Or install globally:",
|
|
1201
|
+
"",
|
|
1202
|
+
...codeBlock(`npx skills add ${getSkillsCliSource(repositoryUrl)} --global`)
|
|
1203
|
+
],
|
|
1204
|
+
"",
|
|
1205
|
+
...skillLines
|
|
1206
|
+
].join("\n");
|
|
1207
|
+
} } };
|
|
1208
|
+
|
|
902
1209
|
//#endregion
|
|
903
1210
|
//#region src/lib/readme/rules/table-of-contents.ts
|
|
904
1211
|
var table_of_contents_default = { "table-of-contents": {
|
|
@@ -912,6 +1219,7 @@ var table_of_contents_default = { "table-of-contents": {
|
|
|
912
1219
|
z.literal(5),
|
|
913
1220
|
z.literal(6)
|
|
914
1221
|
]).optional(),
|
|
1222
|
+
heading: headingSchema,
|
|
915
1223
|
headingLevel: headingLevelSchema
|
|
916
1224
|
}).optional().parse(options);
|
|
917
1225
|
const result = toc(tree, {
|
|
@@ -920,12 +1228,12 @@ var table_of_contents_default = { "table-of-contents": {
|
|
|
920
1228
|
tight: true
|
|
921
1229
|
});
|
|
922
1230
|
if (result.map === void 0) throw new Error("Could not generate table of contents");
|
|
923
|
-
const heading = `${getHeadingPrefix(validOptions?.headingLevel ?? 2)} Table of contents`;
|
|
924
1231
|
const rootWrapper = {
|
|
925
1232
|
children: result.map.children,
|
|
926
1233
|
type: "root"
|
|
927
1234
|
};
|
|
928
|
-
|
|
1235
|
+
const tocString = remark().use(remarkGfm).stringify(rootWrapper).replaceAll("\n\n", "\n");
|
|
1236
|
+
return [...getHeadingLines(validOptions?.heading, validOptions?.headingLevel ?? 2, "Table of contents"), tocString].join("\n");
|
|
929
1237
|
},
|
|
930
1238
|
order: 1
|
|
931
1239
|
} };
|
|
@@ -951,10 +1259,12 @@ var rules_default = {
|
|
|
951
1259
|
...footer_default,
|
|
952
1260
|
...header_default,
|
|
953
1261
|
...install_default,
|
|
1262
|
+
...installation_default,
|
|
954
1263
|
...license_default,
|
|
955
1264
|
...short_description_default,
|
|
956
1265
|
...size_default,
|
|
957
1266
|
...size_table_default,
|
|
1267
|
+
...skills_default,
|
|
958
1268
|
...table_of_contents_default,
|
|
959
1269
|
...title_default,
|
|
960
1270
|
...toc_default
|
|
@@ -1238,19 +1548,19 @@ var mdat_readme_default = "<!-- title -->\n\n<!-- banner -->\n\n<!-- badges -->\
|
|
|
1238
1548
|
|
|
1239
1549
|
//#endregion
|
|
1240
1550
|
//#region src/lib/readme/templates/standard-readme-basic-compound.md?raw
|
|
1241
|
-
var standard_readme_basic_compound_default = "<!-- header -->\n\n
|
|
1551
|
+
var standard_readme_basic_compound_default = "<!-- header -->\n\n<!-- install({ headingLevel: 2 }) -->\n\n## Usage\n\n```ts\n// Code block illustrating common usage.\n// Consider using the <!-- code({ file: \"path/to/example.ts\" }) --> comment as well.\n```\n\n<!-- footer -->\n";
|
|
1242
1552
|
|
|
1243
1553
|
//#endregion
|
|
1244
1554
|
//#region src/lib/readme/templates/standard-readme-basic.md?raw
|
|
1245
|
-
var standard_readme_basic_default = "<!-- title -->\n\n<!-- short-description -->\n\n<!-- table-of-contents -->\n\n
|
|
1555
|
+
var standard_readme_basic_default = "<!-- title -->\n\n<!-- short-description -->\n\n<!-- table-of-contents -->\n\n<!-- install({ headingLevel: 2 }) -->\n\n## Usage\n\n```ts\n// Code block illustrating common usage.\n// Consider using the <!-- code({ file: \"path/to/example.ts\" }) --> comment as well.\n```\n\n<!-- contributing -->\n\n<!-- license -->\n";
|
|
1246
1556
|
|
|
1247
1557
|
//#endregion
|
|
1248
1558
|
//#region src/lib/readme/templates/standard-readme-full-compound.md?raw
|
|
1249
|
-
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
|
|
1559
|
+
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({ headingLevel: 2 }) -->\n\n<!-- dependencies -->\n\n## Usage\n\n```ts\n// Code block illustrating common usage.\n// Consider using the <!-- code({ file: \"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";
|
|
1250
1560
|
|
|
1251
1561
|
//#endregion
|
|
1252
1562
|
//#region src/lib/readme/templates/standard-readme-full.md?raw
|
|
1253
|
-
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
|
|
1563
|
+
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({ headingLevel: 2 }) -->\n\n<!-- dependencies -->\n\n## Usage\n\n```ts\n// Code block illustrating common usage.\n// Consider using the <!-- code({ file: \"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";
|
|
1254
1564
|
|
|
1255
1565
|
//#endregion
|
|
1256
1566
|
//#region src/lib/readme/templates/index.ts
|
|
@@ -1283,6 +1593,8 @@ var templates_default = {
|
|
|
1283
1593
|
|
|
1284
1594
|
//#endregion
|
|
1285
1595
|
//#region src/lib/readme/create.ts
|
|
1596
|
+
const USAGE_HEADING = "\n## Usage\n";
|
|
1597
|
+
const NEXT_SECTION_REGEX = /\n(?:## |<!-- (?:contributing|footer) -->)/v;
|
|
1286
1598
|
/**
|
|
1287
1599
|
* Creates a new readme file interactively.
|
|
1288
1600
|
*
|
|
@@ -1339,7 +1651,8 @@ async function createReadme(options) {
|
|
|
1339
1651
|
expand: true,
|
|
1340
1652
|
template: Object.keys(templates_default)[0]
|
|
1341
1653
|
}, options ?? {});
|
|
1342
|
-
const
|
|
1654
|
+
const baseTemplateString = getTemplateForConfig(resolvedOptions.template, resolvedOptions.compound);
|
|
1655
|
+
const templateString = (await findSkills(process.cwd())).length > 0 ? addSkillsPlaceholder(baseTemplateString) : baseTemplateString;
|
|
1343
1656
|
const readmePath = path.join(resolvedOptions.output, "readme.md");
|
|
1344
1657
|
if (!resolvedOptions.overwrite) {
|
|
1345
1658
|
let exists = true;
|
|
@@ -1358,6 +1671,20 @@ async function createReadme(options) {
|
|
|
1358
1671
|
}
|
|
1359
1672
|
return readmePath;
|
|
1360
1673
|
}
|
|
1674
|
+
/**
|
|
1675
|
+
* Adds a `<!-- skills -->` placeholder to a template, between the end of its
|
|
1676
|
+
* "Usage" section and whatever follows it.
|
|
1677
|
+
*
|
|
1678
|
+
* @throws {Error} If the template has no "Usage" section followed by another
|
|
1679
|
+
* section
|
|
1680
|
+
*/
|
|
1681
|
+
function addSkillsPlaceholder(template) {
|
|
1682
|
+
const usageIndex = template.indexOf(USAGE_HEADING);
|
|
1683
|
+
const nextSectionOffset = usageIndex === -1 ? void 0 : NEXT_SECTION_REGEX.exec(template.slice(usageIndex + 10))?.index;
|
|
1684
|
+
if (nextSectionOffset === void 0) throw new Error("Could not find a \"## Usage\" section followed by another section in the template");
|
|
1685
|
+
const insertIndex = usageIndex + 10 + nextSectionOffset + 1;
|
|
1686
|
+
return `${template.slice(0, insertIndex)}<!-- skills -->\n\n${template.slice(insertIndex)}`;
|
|
1687
|
+
}
|
|
1361
1688
|
function getTemplateForConfig(templateKey, compound) {
|
|
1362
1689
|
if (!Object.hasOwn(templates_default, templateKey)) throw new Error(`Unknown template "${templateKey}". Available templates: ${Object.keys(templates_default).join(", ")}`);
|
|
1363
1690
|
return templates_default[templateKey].content[compound ? "compound" : "explicit"];
|