github-action-readme-generator 1.12.5 → 1.12.7

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/mjs/index.js CHANGED
@@ -11,7 +11,9 @@ import { icons } from "feather-icons";
11
11
  import chalkPkg from "chalk";
12
12
  import { execFileSync, execSync } from "node:child_process";
13
13
  import { EOL } from "node:os";
14
- import { format } from "prettier";
14
+ import * as markdown from "prettier/plugins/markdown";
15
+ import * as yaml from "prettier/plugins/yaml";
16
+ import { format } from "prettier/standalone";
15
17
  import { SVG, registerWindow } from "@svgdotjs/svg.js";
16
18
  import { createSVGWindow } from "svgdom";
17
19
  //#region src/constants.ts
@@ -35,6 +37,29 @@ const README_SECTIONS = [
35
37
  * Represents the file name for the configuration file.
36
38
  */
37
39
  const configFileName = ".ghadocs.json";
40
+ let ConfigKeys = /* @__PURE__ */ function(ConfigKeys) {
41
+ ConfigKeys["Owner"] = "owner";
42
+ ConfigKeys["Repo"] = "repo";
43
+ ConfigKeys["TitlePrefix"] = "title_prefix";
44
+ ConfigKeys["Prettier"] = "prettier";
45
+ ConfigKeys["Save"] = "save";
46
+ ConfigKeys["pathsAction"] = "paths:action";
47
+ ConfigKeys["pathsReadme"] = "paths:readme";
48
+ ConfigKeys["BrandingSvgPath"] = "branding_svg_path";
49
+ ConfigKeys["BrandingAsTitlePrefix"] = "branding_as_title_prefix";
50
+ ConfigKeys["VersioningEnabled"] = "versioning:enabled";
51
+ ConfigKeys["VersioningOverride"] = "versioning:override";
52
+ ConfigKeys["VersioningPrefix"] = "versioning:prefix";
53
+ ConfigKeys["VersioningBranch"] = "versioning:branch";
54
+ ConfigKeys["VersioningSource"] = "versioning:source";
55
+ ConfigKeys["IncludeGithubVersionBadge"] = "versioning:badge";
56
+ ConfigKeys["DebugNconf"] = "debug:nconf";
57
+ ConfigKeys["DebugReadme"] = "debug:readme";
58
+ ConfigKeys["DebugConfig"] = "debug:config";
59
+ ConfigKeys["DebugAction"] = "debug:action";
60
+ ConfigKeys["DebugGithub"] = "debug:github";
61
+ return ConfigKeys;
62
+ }({});
38
63
  /**
39
64
  * Represents the default brand color.
40
65
  */
@@ -666,6 +691,21 @@ function getVersionFromPackageJson(actionDir, log) {
666
691
  log.debug(`package.json not found at ${packageJsonPath}`);
667
692
  }
668
693
  }
694
+ /**
695
+ * Whether the generated README should be run through prettier before it is
696
+ * written.
697
+ *
698
+ * Unset means enabled, matching `action.yml`'s `pretty` default of `"true"`.
699
+ * The value arrives as a real boolean from `.ghadocs.json` and as a string from
700
+ * action inputs and CLI args, so both spellings are accepted; anything else
701
+ * (`false`, `"false"`, `"no"`, …) disables formatting.
702
+ * @param {Inputs} inputs - The resolved inputs to read the flag from.
703
+ * @returns {boolean} True when prettier formatting should run.
704
+ */
705
+ function isPrettierEnabled(inputs) {
706
+ const prettier = inputs.config.get("prettier");
707
+ return prettier === void 0 || prettier === true || prettier === "true";
708
+ }
669
709
  function getCurrentVersionString(inputs) {
670
710
  let versionString = "";
671
711
  const log = new LogTask("getCurrentVersionString");
@@ -740,16 +780,32 @@ function lastIndexOfRegex(str, providedRegex) {
740
780
  }
741
781
  //#endregion
742
782
  //#region src/prettier.ts
783
+ const log$1 = new LogTask("prettier");
743
784
  /**
744
- * This TypeScript code exports three functions: `formatYaml`, `formatMarkdown`, and `wrapDescription`.
785
+ * The languages a GitHub Action's README is known in advance to contain.
745
786
  *
746
- * - `formatYaml` takes a YAML string and an optional filepath as parameters and uses the `prettier` library to format the YAML code. It returns the formatted YAML string.
747
- * - `formatMarkdown` takes a Markdown string and an optional filepath as parameters and uses the `prettier` library to format the Markdown code. It returns the formatted Markdown string.
748
- * - `wrapDescription` takes a string value, an array of content, and an optional prefix as parameters. It wraps the description text with the specified prefix and formats it using `prettier`. It returns the updated content array with the formatted description lines.
787
+ * `markdown` is the parser itself. `yaml` covers the workflow snippets that are
788
+ * what an action README is mostly made of — and the ```yaml usage block this
789
+ * tool generates.
749
790
  *
750
- * The code utilizes the `prettier` library for code formatting and the `LogTask` class for logging purposes.
791
+ * Nothing else is bundled. `embeddedLanguageFormatting: 'auto'` reformats fenced
792
+ * code blocks, and `standalone` silently leaves a fence alone when its plugin is
793
+ * absent, so this list is exactly the set of fences the tool reformats. That is
794
+ * the intended contract, not a gap: this tool exists for GitHub Actions, and a
795
+ * fence in some other language is prose the action's author wrote, which the
796
+ * tool has no business rewriting.
797
+ *
798
+ * The bar for adding one: a language that action READMEs are known in advance
799
+ * to contain, not one they could. Every plugin here is weight in a binary that
800
+ * ships to every consumer, spent to reformat code the action's author wrote.
801
+ *
802
+ * Extending this per-project — naming extra prettier plugins in configuration —
803
+ * is tracked separately; see the README.
804
+ *
805
+ * Exported so __tests__/prettier.test.ts derives the formatted-parser set from
806
+ * this array rather than restating dependency-owned parser metadata.
751
807
  */
752
- const log$1 = new LogTask("prettier");
808
+ const plugins = [markdown, yaml];
753
809
  /**
754
810
  * Formats a Markdown string using `prettier`.
755
811
  * @param {string} value - The Markdown string to format.
@@ -761,6 +817,7 @@ async function formatMarkdown(value, filepath) {
761
817
  semi: false,
762
818
  parser: "markdown",
763
819
  embeddedLanguageFormatting: "auto",
820
+ plugins,
764
821
  ...filepath ? { filepath } : {}
765
822
  });
766
823
  }
@@ -778,7 +835,8 @@ async function wrapDescription(value, content, prefix = " # ") {
778
835
  formattedString = await format(value, {
779
836
  semi: false,
780
837
  parser: "markdown",
781
- proseWrap: "always"
838
+ proseWrap: "always",
839
+ plugins
782
840
  });
783
841
  } catch (error) {
784
842
  log$1.error(`${String(error)}`);
@@ -870,10 +928,13 @@ var ReadmeEditor = class {
870
928
  }
871
929
  /**
872
930
  * Dumps the modified content back to the README file.
931
+ * @param {boolean} [prettier=true] - Run the result through prettier before
932
+ * writing. Callers pass the resolved `pretty` input; it defaults to true so
933
+ * constructing a ReadmeEditor directly keeps the formatting behaviour.
873
934
  * @returns {Promise<void>}
874
935
  */
875
- async dumpToFile() {
876
- const content = await formatMarkdown(this.fileContent);
936
+ async dumpToFile(prettier = true) {
937
+ const content = prettier ? await formatMarkdown(this.fileContent) : this.fileContent;
877
938
  if (process.env.GITHUB_ACTIONS) core.setOutput("readme_after", content);
878
939
  return fs.promises.writeFile(this.filePath, content, "utf8");
879
940
  }
@@ -1134,7 +1195,9 @@ const ConfigKeysInputsMap = {
1134
1195
  owner: "owner",
1135
1196
  repo: "repo",
1136
1197
  title_prefix: "title_prefix",
1137
- pretty: "prettier"
1198
+ pretty: "prettier",
1199
+ debug_config: "debug:config",
1200
+ debug_nconf: "debug:nconf"
1138
1201
  };
1139
1202
  function transformGitHubInputsToArgv(log, _config, obj) {
1140
1203
  /** The obj.key is always in lowercase, but it checks for it without case sensitivity */
@@ -1198,6 +1261,224 @@ function collectAllDefaultValuesFromAction(log, providedMetaActionPath, provided
1198
1261
  return {};
1199
1262
  }
1200
1263
  }
1264
+ /** Key fragments whose values are masked before the resolved config is printed. */
1265
+ const SENSITIVE_KEY_PATTERN = /auth|credential|key|passw|secret|token/i;
1266
+ /** Stand-in written in place of a masked value. */
1267
+ const REDACTED = "***REDACTED***";
1268
+ /**
1269
+ * `sections` is the one key holding an array, and its domain is the finite
1270
+ * {@link README_SECTIONS} set — `updateSection` ignores anything else. Naming
1271
+ * the leaf after that domain, rather than calling it a generic array, is what
1272
+ * lets an unrecognised entry be masked instead of printed.
1273
+ */
1274
+ const SECTION_LIST_KEYS = /* @__PURE__ */ new Set(["sections"]);
1275
+ /**
1276
+ * Keys whose declared domain is a boolean. No code path reads the bytes of a
1277
+ * value under one of these: the readers either compare against boolean literals
1278
+ * (`save.ts` on `=== true`; `isPrettierEnabled`, `getCurrentVersionString` and
1279
+ * `isDebugConfigEnabled` on `=== true || === 'true'`) or gate on truthiness
1280
+ * (`update-title.ts`, `update-badges.ts`), which consumes the value's
1281
+ * truthiness and nothing else.
1282
+ *
1283
+ * The domain comes from the key's contract, not from how its reader happens to
1284
+ * test it — `action.yml` declares `branding_as_title_prefix` as
1285
+ * `type: boolean` and `include_github_version_badge` with a boolean default,
1286
+ * so a string under either is malformed input whose content is never read.
1287
+ */
1288
+ const BOOLEAN_KEYS = /* @__PURE__ */ new Set([
1289
+ "save",
1290
+ "prettier",
1291
+ "versioning:enabled",
1292
+ "branding_as_title_prefix",
1293
+ "versioning:badge",
1294
+ "debug:config",
1295
+ "debug:nconf"
1296
+ ]);
1297
+ /** The boolean forms nconf can hand back, parsed or still as a string. */
1298
+ const BOOLEAN_VALUES = /* @__PURE__ */ new Set([
1299
+ true,
1300
+ false,
1301
+ "true",
1302
+ "false"
1303
+ ]);
1304
+ /**
1305
+ * `versioning:source` selects a detection strategy from a closed set;
1306
+ * `getCurrentVersionString` switches over these and treats anything else as
1307
+ * `git-tag`, so a value outside the set is never used for its content.
1308
+ */
1309
+ const VERSION_SOURCE_KEYS = /* @__PURE__ */ new Set(["versioning:source"]);
1310
+ /** The strategies `getCurrentVersionString` switches over. */
1311
+ const VERSION_SOURCES = /* @__PURE__ */ new Set([
1312
+ "git-tag",
1313
+ "git-branch",
1314
+ "git-sha",
1315
+ "package-json",
1316
+ "explicit"
1317
+ ]);
1318
+ /**
1319
+ * `image_generated` caches the branding the SVG on disk was drawn from.
1320
+ *
1321
+ * It is the one key the tool writes and reads back that `ConfigKeys` does not
1322
+ * declare: `generateImgMarkup` sets it to `${icon}${color}` and, on a later
1323
+ * run, regenerates the SVG unless the saved value equals that same string.
1324
+ */
1325
+ const BRANDING_HASH_KEYS = /* @__PURE__ */ new Set(["image_generated"]);
1326
+ /**
1327
+ * Whether a value is a branding hash `generateImgMarkup`'s comparison can match.
1328
+ *
1329
+ * The icon and colour it concatenates come from closed sets, so the producible
1330
+ * hashes are a finite domain — the same reason `sections` and `versioning:source`
1331
+ * are validated against their contents rather than their shape. The two sets are
1332
+ * joined without a separator, so the colour is recovered by suffix.
1333
+ * @param {unknown} value - The value found under a branding-hash key.
1334
+ * @returns {boolean} True when the value is an icon name followed by a colour.
1335
+ */
1336
+ function isBrandingHash(value) {
1337
+ if (typeof value !== "string") return false;
1338
+ return GITHUB_ACTIONS_BRANDING_COLORS.some((color) => value.endsWith(color) && GITHUB_ACTIONS_BRANDING_ICONS.has(value.slice(0, -color.length)));
1339
+ }
1340
+ /**
1341
+ * Splits colon-separated config paths into the nested shape nconf resolves them
1342
+ * to, so `versioning:prefix` becomes `{ versioning: { prefix: 'scalar' } }`.
1343
+ * @param {readonly string[]} paths - Colon-separated key paths.
1344
+ * @returns {KnownKeyTree} The paths as a nested tree of declared shapes.
1345
+ */
1346
+ function buildKnownKeyTree(paths) {
1347
+ const tree = {};
1348
+ for (const path of paths) {
1349
+ const segments = path.split(":");
1350
+ let node = tree;
1351
+ for (const [index, segment] of segments.entries()) {
1352
+ if (index === segments.length - 1) {
1353
+ if (SECTION_LIST_KEYS.has(path)) node[segment] = "section-names";
1354
+ else if (BOOLEAN_KEYS.has(path)) node[segment] = "boolean";
1355
+ else if (VERSION_SOURCE_KEYS.has(path)) node[segment] = "version-source";
1356
+ else if (BRANDING_HASH_KEYS.has(path)) node[segment] = "branding-hash";
1357
+ else node[segment] = "scalar";
1358
+ break;
1359
+ }
1360
+ const next = node[segment];
1361
+ node = next === void 0 || typeof next === "string" ? node[segment] = {} : next;
1362
+ }
1363
+ }
1364
+ return tree;
1365
+ }
1366
+ /**
1367
+ * Canonical keys {@link ConfigKeys} declares that nothing in `src` reads, and
1368
+ * that no `action.yml` input or CLI flag can set. A value can only reach one of
1369
+ * them through `.ghadocs.json`, and no code path consumes it once there — so it
1370
+ * is a caller's value, not the tool's, and it is masked like any other unknown
1371
+ * key. Drop an entry from here once a reader for it lands.
1372
+ */
1373
+ const UNREAD_CONFIG_KEYS = /* @__PURE__ */ new Set([
1374
+ "debug:readme",
1375
+ "debug:action",
1376
+ "debug:github"
1377
+ ]);
1378
+ /**
1379
+ * Every key this tool resolves a value out of: the canonical config keys it
1380
+ * actually reads, plus `sections`, which `Inputs` sets and reads directly.
1381
+ *
1382
+ * The action-input and CLI spellings in {@link ConfigKeysInputsMap} are
1383
+ * deliberately absent. Those are input spellings, not resolved keys — the file
1384
+ * store performs no mapping, so `{"action": "…"}` in `.ghadocs.json` leaves a
1385
+ * bare `action` key that nothing ever reads, and listing it here would have
1386
+ * printed that value. Nothing is lost by masking them: when a value does arrive
1387
+ * through an alias, nconf's argv store records the canonical key too, so the
1388
+ * dump still shows it under `paths:action` / `debug:config`.
1389
+ */
1390
+ const KNOWN_KEY_TREE = buildKnownKeyTree([
1391
+ ...Object.values(ConfigKeys).filter((key) => !UNREAD_CONFIG_KEYS.has(key)),
1392
+ "sections",
1393
+ ...BRANDING_HASH_KEYS
1394
+ ]);
1395
+ /**
1396
+ * Whether a value is shaped like something a known leaf key can hold: a scalar,
1397
+ * or an array of scalars.
1398
+ *
1399
+ * Every key in {@link KNOWN_KEY_TREE} holds a scalar except `sections`, which
1400
+ * holds a string array. Anything richer arriving under one of those names came
1401
+ * from a caller, not from this tool, and nothing here reads it.
1402
+ * @param {unknown} value - The value found under a known leaf key.
1403
+ * @returns {boolean} True when the value is a scalar or an array of scalars.
1404
+ */
1405
+ function isScalarLeafValue(value) {
1406
+ return !Array.isArray(value) && (value === null || typeof value !== "object");
1407
+ }
1408
+ /**
1409
+ * Whether a value is an array holding only scalars — the shape `sections` has.
1410
+ * @param {unknown} value - The value found under an array-valued key.
1411
+ * @returns {boolean} True for an array with no object or array elements.
1412
+ */
1413
+ function isSectionNameList(value) {
1414
+ const names = README_SECTIONS;
1415
+ return Array.isArray(value) && value.every((entry) => names.includes(entry));
1416
+ }
1417
+ /**
1418
+ * Masks every value the tool does not itself read, plus anything under a
1419
+ * sensitive-looking key, before the resolved config is printed.
1420
+ *
1421
+ * `action.yml` declares no secret input, and the env store admits only
1422
+ * `INPUT_*` keys, so ambient variables such as `GITHUB_TOKEN` never reach the
1423
+ * config. A caller can still land one: GitHub sets `INPUT_*` for every key
1424
+ * under `with:`, declared or not, and `.ghadocs.json` and CLI args are
1425
+ * user-controlled. The dump exists to be pasted into bug reports, so it masks
1426
+ * rather than relying on the runner's own secret masking.
1427
+ *
1428
+ * Because those three sources admit arbitrary keys, a name heuristic alone
1429
+ * cannot make the dump safe — `--webhook=https://hooks.example/...` carries a
1430
+ * credential under a name no pattern would flag. So the allow-list is the
1431
+ * primary defence: a key the tool reads is printed, anything else is masked.
1432
+ * That still shows a reporter which unexpected keys were supplied, without
1433
+ * printing what they held. `SENSITIVE_KEY_PATTERN` stays as a second line,
1434
+ * covering a future known key whose value is genuinely secret.
1435
+ *
1436
+ * Values under nconf and yargs bookkeeping keys (`_`, `$0`) are masked by the
1437
+ * same rule; they are not configuration and nothing reads them.
1438
+ *
1439
+ * Recurses so nested groups (`versioning:*`, `paths:*`) are covered. Arrays are
1440
+ * walked; a masked key's value is replaced whole rather than descended into.
1441
+ * @param {unknown} value - The resolved config, or a nested part of it.
1442
+ * @param {KnownKeyTree | true} known - Allow-list for this level; `true` means
1443
+ * every key below is already within a known leaf's value.
1444
+ * @returns {unknown} A copy with unknown and sensitive values masked.
1445
+ */
1446
+ function redactSensitiveValues(value, known = KNOWN_KEY_TREE) {
1447
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return value;
1448
+ return Object.fromEntries(Object.entries(value).map(([key, entry]) => {
1449
+ if (SENSITIVE_KEY_PATTERN.test(key)) return [key, REDACTED];
1450
+ const branch = known[key];
1451
+ switch (branch) {
1452
+ case void 0: return [key, REDACTED];
1453
+ case "scalar": return [key, isScalarLeafValue(entry) ? entry : REDACTED];
1454
+ case "section-names": return [key, isSectionNameList(entry) ? entry : REDACTED];
1455
+ case "boolean": return [key, BOOLEAN_VALUES.has(entry) ? entry : REDACTED];
1456
+ case "version-source": return [key, VERSION_SOURCES.has(entry) ? entry : REDACTED];
1457
+ case "branding-hash": return [key, isBrandingHash(entry) ? entry : REDACTED];
1458
+ default: return [key, entry !== null && typeof entry === "object" && !Array.isArray(entry) ? redactSensitiveValues(entry, branch) : REDACTED];
1459
+ }
1460
+ }));
1461
+ }
1462
+ /**
1463
+ * Whether the resolved nconf object was asked for.
1464
+ *
1465
+ * Two flags carry the identical promise "Print out the resolved nconf object
1466
+ * with all values" — `--debug_config` and the older `--debug_nconf` — so either
1467
+ * triggers the dump rather than one of them continuing to be inert.
1468
+ *
1469
+ * Unset means off, the opposite of `pretty`: this is a diagnostic, and neither
1470
+ * flag is declared in `action.yml`. The value arrives as a real boolean from
1471
+ * `.ghadocs.json` and as a string from env and CLI args, so both spellings are
1472
+ * accepted.
1473
+ * @param {ProviderInstance} config - The resolved config instance.
1474
+ * @returns {boolean} True when the resolved config should be printed.
1475
+ */
1476
+ function isDebugConfigEnabled(config) {
1477
+ return ["debug:config", "debug:nconf"].some((key) => {
1478
+ const value = config.get(key);
1479
+ return value === true || value === "true";
1480
+ });
1481
+ }
1201
1482
  /**
1202
1483
  * Loads the configuration
1203
1484
  *
@@ -1207,6 +1488,7 @@ function loadConfig(log, providedConfig, configFilePath) {
1207
1488
  log.debug("Loading config from env and argv");
1208
1489
  const config = providedConfig ?? new Provider();
1209
1490
  if (process.env.GITHUB_ACTION === "true") log.info("Running in GitHub action");
1491
+ config.argv(argvOptions);
1210
1492
  if (configFilePath) if (fs.existsSync(configFilePath)) {
1211
1493
  log.info(`Config file found: ${configFilePath}`);
1212
1494
  config.file(configFilePath);
@@ -1217,7 +1499,7 @@ function loadConfig(log, providedConfig, configFilePath) {
1217
1499
  transform: (obj) => {
1218
1500
  return transformGitHubInputsToArgv(log, config, obj);
1219
1501
  }
1220
- }).argv(argvOptions);
1502
+ });
1221
1503
  return config;
1222
1504
  }
1223
1505
  /**
@@ -1321,7 +1603,13 @@ var Inputs = class {
1321
1603
  this.configPath = inputContext.configPath ?? path$1.resolve(".ghadocs.json");
1322
1604
  this.config = inputContext.config ?? new Provider();
1323
1605
  loadConfig(log, this.config, this.configPath);
1324
- loadDefaultConfig(log, this.config);
1606
+ try {
1607
+ loadDefaultConfig(log, this.config);
1608
+ } catch (error) {
1609
+ this.dumpResolvedConfig();
1610
+ throw error;
1611
+ }
1612
+ this.dumpResolvedConfig();
1325
1613
  loadRequiredConfig(log, this.config);
1326
1614
  this.action = inputContext.action ?? loadAction(log, this.config.get("paths:action"));
1327
1615
  this.config.set("sections", inputContext.sections ?? this.config.get("sections"));
@@ -1341,9 +1629,23 @@ var Inputs = class {
1341
1629
  */
1342
1630
  this.repo = inputContext.repo ?? this.config.get("repo");
1343
1631
  }
1632
+ /**
1633
+ * Prints the resolved configuration when `--debug_config` (or the older
1634
+ * `--debug_nconf`) asked for it.
1635
+ *
1636
+ * Called from two places in the constructor because both of the failures this
1637
+ * flag diagnoses throw, and each throws from a different call — see the
1638
+ * comments at those call sites. Rerunning it after a successful
1639
+ * `loadDefaultConfig` is the only path that reaches the second call, so no
1640
+ * run prints the dump twice.
1641
+ * @returns {void}
1642
+ */
1643
+ dumpResolvedConfig() {
1644
+ if (isDebugConfigEnabled(this.config)) this.log.info(`Resolved config:\n${this.stringify()}`);
1645
+ }
1344
1646
  stringify() {
1345
1647
  if (this?.config) try {
1346
- return YAML.stringify(this.config.get());
1648
+ return YAML.stringify(redactSensitiveValues(this.config.get()));
1347
1649
  } catch (error) {
1348
1650
  this.log.error(`${String(error)}`);
1349
1651
  }
@@ -1724,7 +2026,7 @@ function updateDescription(sectionToken, inputs) {
1724
2026
  * @returns The escaped text.
1725
2027
  */
1726
2028
  function markdownEscapeTableCell(text) {
1727
- return text.replaceAll("\n", "<br />").replaceAll("|", "\\|");
2029
+ return text.replaceAll("\n", "<br />").replaceAll(/\\+(?=\|)/g, (slashes) => slashes.repeat(2)).replaceAll("|", "\\|");
1728
2030
  }
1729
2031
  /**
1730
2032
  * Escapes inline code blocks in a Markdown string.
@@ -1831,13 +2133,12 @@ function updateInputs(sectionToken, inputs) {
1831
2133
  for (const key of Object.keys(vars)) {
1832
2134
  const values = vars[key];
1833
2135
  let description = values?.description ?? "";
1834
- const matches = /(.*?)\n\n([Ss]*)/.exec(description);
1835
- if (matches && matches.length >= 2) description = matches[1] || description;
1836
- description = description.trim().replace("\n", "<br />");
2136
+ description = description.trim().split("\n\n")[0] ?? "";
2137
+ description = description.replaceAll("\n", "<br />");
1837
2138
  const row = [
1838
2139
  rowHeader(key),
1839
2140
  description,
1840
- values?.default ? `<code>${values.default}</code>` : "",
2141
+ values?.default === void 0 || values.default === "" ? "" : `<code>${values.default}</code>`,
1841
2142
  values?.required ? "**true**" : "__false__"
1842
2143
  ];
1843
2144
  log.debug(JSON.stringify(row));
@@ -1847,7 +2148,10 @@ function updateInputs(sectionToken, inputs) {
1847
2148
  log.info(`Action has ${tI} total ${sectionToken}`);
1848
2149
  inputs.readmeEditor.updateSection(sectionToken, content);
1849
2150
  log.success();
1850
- } else log.debug(`Action has no ${sectionToken}`);
2151
+ } else {
2152
+ log.debug(`Action has no ${sectionToken}`);
2153
+ inputs.readmeEditor.updateSection(sectionToken, content);
2154
+ }
1851
2155
  const ret = {};
1852
2156
  ret[sectionToken] = content.join("\n");
1853
2157
  return ret;
@@ -1873,9 +2177,8 @@ function updateOutputs(sectionToken, inputs) {
1873
2177
  for (const key of Object.keys(vars)) {
1874
2178
  const values = vars[key];
1875
2179
  let description = values?.description ?? "";
1876
- const matches = /(.*?)\n\n([Ss]*)/.exec(description);
1877
- if (matches && matches.length >= 2) description = matches[1] || description;
1878
- description = description.trim().replace("\n", "<br />");
2180
+ description = description.trim().split("\n\n")[0] ?? "";
2181
+ description = description.replaceAll("\n", "<br />");
1879
2182
  const value = values?.value ? `\`${values.value}\`` : "";
1880
2183
  const row = [
1881
2184
  rowHeader(key),
@@ -1889,7 +2192,10 @@ function updateOutputs(sectionToken, inputs) {
1889
2192
  log.info(`Action has ${tI} total ${sectionToken}`);
1890
2193
  inputs.readmeEditor.updateSection(sectionToken, content);
1891
2194
  log.success();
1892
- } else log.debug(`Action has no ${sectionToken}`);
2195
+ } else {
2196
+ log.debug(`Action has no ${sectionToken}`);
2197
+ inputs.readmeEditor.updateSection(sectionToken, content);
2198
+ }
1893
2199
  const ret = {};
1894
2200
  ret[sectionToken] = content.join("\n");
1895
2201
  return ret;
@@ -2061,7 +2367,7 @@ var ReadmeGenerator = class {
2061
2367
  const sectionPromises = this.updateSections(providedSections);
2062
2368
  const sections = await this.resolveUpdates(sectionPromises);
2063
2369
  this.outputSections(sections);
2064
- return this.inputs.readmeEditor.dumpToFile();
2370
+ return this.inputs.readmeEditor.dumpToFile(isPrettierEnabled(this.inputs));
2065
2371
  }
2066
2372
  };
2067
2373
  //#endregion
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "github-action-readme-generator",
3
3
  "displayName": "bitflight-devops/github-action-readme-generator",
4
- "version": "1.12.5",
4
+ "version": "1.12.7",
5
5
  "description": "The docs generator for GitHub Actions. Auto-syncs action.yml to README.md with 8 sections: inputs, outputs, usage, badges, branding & more. Works as CLI or GitHub Action.",
6
6
  "keywords": [
7
7
  "github-actions",