github-action-readme-generator 1.12.9 → 2.0.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/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## [2.0.0](https://github.com/bitflight-devops/github-action-readme-generator/compare/v1.12.9...v2.0.0) (2026-09-25)
2
+
3
+ ### ⚠ BREAKING CHANGES
4
+
5
+ * **prettier:** format only the spans between section markers (#690)
6
+
7
+ ### Bug Fixes
8
+
9
+ * **prettier:** format only the spans between section markers ([#690](https://github.com/bitflight-devops/github-action-readme-generator/issues/690)) ([f48b40d](https://github.com/bitflight-devops/github-action-readme-generator/commit/f48b40d7f03ec632f4c10957c7a2130f2d20cc7f))
10
+
1
11
  ## [1.12.9](https://github.com/bitflight-devops/github-action-readme-generator/compare/v1.12.8...v1.12.9) (2026-09-22)
2
12
 
3
13
 
package/README.md CHANGED
@@ -70,11 +70,12 @@ sections whose markers you added are touched.
70
70
  **Text outside the markers is yours and stays yours.** The tool replaces only
71
71
  the span between a `start`/`end` pair.
72
72
 
73
- **With `pretty` on (the default), the whole file is reformatted.** That is the
74
- one exception to the rule above: prettier runs over the entire README, so a
75
- first run on a file that was not already prettier-formatted will also reflow
76
- prose the tool did not generate. Run with `--pretty=false` if you would rather
77
- it left your formatting alone.
73
+ **`pretty` formats the generated sections and nothing else.** With it on (the
74
+ default) prettier runs over each replaced span on its own — it pads the tables
75
+ it generated and tidies the usage block, and it never reaches your prose. A
76
+ first run on a file that was not already prettier-formatted changes the marker
77
+ sections and no other line. Run with `--pretty=false` to have the generated
78
+ sections written exactly as the tool composed them.
78
79
 
79
80
  **Only yaml and markdown code blocks are reformatted; every other fence is left
80
81
  as you wrote it.** Formatting needs a prettier plugin per language, and this tool
@@ -105,7 +106,7 @@ apply to the CLI and the Action alike.
105
106
  | --paths:readme, --readme | Path to the README file | `README.md` |
106
107
  | --owner | The GitHub Action repository owner | autodetected |
107
108
  | --repo | The GitHub Action repository name | autodetected |
108
- | --prettier, --pretty | Format the markdown using prettier formatter | `true` |
109
+ | --prettier, --pretty | Format the generated sections using the prettier formatter | `true` |
109
110
  | --versioning:enabled, --versioning | Enable the update of the usage version to match the latest version | `true` |
110
111
  | --versioning:source, --version_source | How to detect the version: `git-tag`, `git-branch`, `git-sha`, `package-json`, `explicit` | `git-tag` |
111
112
  | --versioning:override, --version_override, --setversion | Set a specific version to display in the README.md | unset |
@@ -118,7 +119,8 @@ apply to the CLI and the Action alike.
118
119
  | --save | Save this config to `.ghadocs.json` | `false` |
119
120
  | --debug_config, --debug_nconf | Print out the resolved config with all values, then continue | off |
120
121
 
121
- To turn formatting off, pass the flag explicitly:
122
+ To write the generated sections exactly as the tool composed them, pass the flag
123
+ explicitly:
122
124
 
123
125
  ```sh
124
126
  npx github-action-readme-generator --pretty=false
@@ -267,7 +269,7 @@ value replaced by `***REDACTED***`. Keys whose names look sensitive (`auth`,
267
269
  <!-- start usage -->
268
270
 
269
271
  ```yaml
270
- - uses: bitflight-devops/github-action-readme-generator@v1.12.8
272
+ - uses: bitflight-devops/github-action-readme-generator@v1.12.9
271
273
  with:
272
274
  # Description: The absolute or relative path to the `action.yml` file to read in
273
275
  # from.
@@ -297,7 +299,9 @@ value replaced by `***REDACTED***`. Keys whose names look sensitive (`auth`,
297
299
  # Default: false
298
300
  save: ""
299
301
 
300
- # Description: Use `prettier` to pretty print the new README.md file
302
+ # Description: Use `prettier` to pretty print the sections this tool generates.
303
+ # Only the content between a section's start and end markers is formatted. The
304
+ # rest of the README.md file is left byte for byte as it was.
301
305
  #
302
306
  # Default: true
303
307
  pretty: ""
@@ -389,7 +393,7 @@ value replaced by `***REDACTED***`. Keys whose names look sensitive (`auth`,
389
393
  | <b><code>owner</code></b> | The GitHub Action repository owner, this field is autodetected by default.<br />Example: <code>bitflight-devops</code> or <code>your-gh-username</code> | | **false** |
390
394
  | <b><code>repo</code></b> | The GitHub Action repository name, this field is autodetected by default.<br />Example: <code>github-action-readme-generator</code> | | **false** |
391
395
  | <b><code>save</code></b> | Save the provided values in a <code>.ghadocs.json</code> file.<br />This will update any existing <code>.ghadocs.json</code> file that is in place. | <code>false</code> | **false** |
392
- | <b><code>pretty</code></b> | Use <code>prettier</code> to pretty print the new README.md file | <code>true</code> | **false** |
396
+ | <b><code>pretty</code></b> | Use <code>prettier</code> to pretty print the sections this tool generates.<br />Only the content between a section's start and end markers is formatted.<br />The rest of the README.md file is left byte for byte as it was. | <code>true</code> | **false** |
393
397
  | <b><code>versioning_enabled</code></b> | Enable the update of the usage version in the <code>uses:</code> example.<br />The version comes from whichever <code>version_source</code> selects. The default, <code>git-tag</code>, uses the latest git tag, and falls back to <code>package.json</code>, then to <code>$npm_package_version</code>, then to <code>0.0.0</code> when no tag is found — a shallow or tagless checkout takes that path.<br />Output if your action repo is <code>reviewdog/action-eslint</code> and the latest tag is <code>v1.0.1</code>:<br /><code>uses: reviewdog/action-eslint@v1.0.1</code> | <code>true</code> | **false** |
394
398
  | <b><code>version_override</code></b> | Set a specific version to display in the README.md, maybe you want to use a major or minor version | | **false** |
395
399
  | <b><code>version_prefix</code></b> | Prefix the version with this value, if it isn't already prefixed | <code>v</code> | **false** |
package/action.yml CHANGED
@@ -36,7 +36,9 @@ inputs:
36
36
  default: false
37
37
  pretty:
38
38
  description: |
39
- Use `prettier` to pretty print the new README.md file
39
+ Use `prettier` to pretty print the sections this tool generates.
40
+ Only the content between a section's start and end markers is formatted.
41
+ The rest of the README.md file is left byte for byte as it was.
40
42
  required: false
41
43
  default: 'true'
42
44
  versioning_enabled:
package/dist/bin/index.js CHANGED
@@ -18,7 +18,7 @@ import { fileURLToPath } from "node:url";
18
18
  import * as child from "child_process";
19
19
  import { setTimeout as setTimeout$1 } from "timers";
20
20
  import process$1 from "node:process";
21
- import os$1, { EOL as EOL$1 } from "node:os";
21
+ import os$1 from "node:os";
22
22
  import tty from "node:tty";
23
23
  import { execFileSync, execSync } from "node:child_process";
24
24
  //#region \0rolldown/runtime.js
@@ -67023,14 +67023,51 @@ const startTokenFormat = "(^|[^`\\\\])<!--\\s+start\\s+%s\\s+-->";
67023
67023
  * The format for the end token of a section.
67024
67024
  */
67025
67025
  const endTokenFormat = "(^|[^`\\\\])<!--\\s+end\\s+%s\\s+-->";
67026
+ /**
67027
+ * Lays out section content the way it sits between its markers.
67028
+ * @param {string} content - The trimmed section content.
67029
+ * @param {boolean} addNewlines - Whether to pad the content with newlines.
67030
+ * @returns {string} - The text that goes between the markers.
67031
+ */
67032
+ function layoutSpan(content, addNewlines) {
67033
+ return addNewlines ? `\n\n${content}\n` : content;
67034
+ }
67035
+ /**
67036
+ * True when every line break in `text` is CRLF, and there is at least one.
67037
+ *
67038
+ * Only a document that is CRLF throughout is edited as CRLF. Removing the `\r`
67039
+ * before each `\n` and adding it back is then an exact round trip, so the
67040
+ * bytes outside the markers survive. A document that mixes the two is left as
67041
+ * it is, since no single ending would reproduce it.
67042
+ * @param {string} text - The document.
67043
+ * @returns {boolean} - Whether the document uses CRLF line endings.
67044
+ */
67045
+ function usesCrlf(text) {
67046
+ return text.includes("\r\n") && !/(^|[^\r])\n/.test(text);
67047
+ }
67026
67048
  var ReadmeEditor = class {
67027
67049
  log = new LogTask("ReadmeEditor");
67028
67050
  /**
67029
67051
  * The path to the README file.
67030
67052
  */
67031
67053
  filePath;
67054
+ /**
67055
+ * The document with LF line endings, whatever the file uses. Every edit and
67056
+ * every formatter pass works on LF; `dumpToFile` restores the file's own
67057
+ * ending on the way out.
67058
+ */
67032
67059
  fileContent;
67033
67060
  /**
67061
+ * Whether the file on disk is CRLF throughout — see `usesCrlf`.
67062
+ */
67063
+ crlf = false;
67064
+ /**
67065
+ * The padded sections this editor has replaced, each against the content it
67066
+ * wrote. `dumpToFile` formats these spans and nothing else — see
67067
+ * `formatUpdatedSections`.
67068
+ */
67069
+ updatedSections = /* @__PURE__ */ new Map();
67070
+ /**
67034
67071
  * Creates a new instance of `ReadmeEditor`.
67035
67072
  * @param {string} filePath - The path to the README file.
67036
67073
  */
@@ -67038,15 +67075,17 @@ var ReadmeEditor = class {
67038
67075
  this.filePath = filePath;
67039
67076
  try {
67040
67077
  fs$3.accessSync(filePath);
67041
- this.fileContent = fs$3.readFileSync(filePath, "utf8");
67042
- if (process.env.GITHUB_ACTIONS) setOutput("readme_before", this.fileContent);
67078
+ const raw = fs$3.readFileSync(filePath, "utf8");
67079
+ if (process.env.GITHUB_ACTIONS) setOutput("readme_before", raw);
67080
+ this.crlf = usesCrlf(raw);
67081
+ this.fileContent = this.crlf ? raw.replaceAll("\r\n", "\n") : raw;
67043
67082
  } catch (error) {
67044
67083
  this.log.fail(`Readme at '${filePath}' does not exist.`);
67045
67084
  throw error;
67046
67085
  }
67047
67086
  }
67048
67087
  /**
67049
- * Gets the current README content.
67088
+ * Gets the current README content, with LF line endings.
67050
67089
  * @returns {string} - The README file content.
67051
67090
  */
67052
67091
  getReadmeContent() {
@@ -67081,24 +67120,66 @@ var ReadmeEditor = class {
67081
67120
  */
67082
67121
  updateSection(name, providedContent, addNewlines = true) {
67083
67122
  const log = new LogTask(name);
67084
- const content = (Array.isArray(providedContent) ? providedContent.join(EOL$1) : providedContent ?? "").trim();
67123
+ const content = (Array.isArray(providedContent) ? providedContent.join("\n") : providedContent ?? "").replaceAll("\r\n", "\n").trim();
67085
67124
  log.info(`Looking for the ${name} token in ${this.filePath}`);
67086
67125
  const [startIndex, stopIndex] = this.getTokenIndexes(name, log);
67087
67126
  if (startIndex && stopIndex) {
67088
67127
  const beforeContent = this.fileContent.slice(0, startIndex);
67089
67128
  const afterContent = this.fileContent.slice(stopIndex);
67090
- this.fileContent = addNewlines ? `${beforeContent}\n\n${content}\n${afterContent}` : `${beforeContent}${content}${afterContent}`;
67129
+ this.fileContent = `${beforeContent}${layoutSpan(content, addNewlines)}${afterContent}`;
67130
+ if (addNewlines) this.updatedSections.set(name, content);
67091
67131
  }
67092
67132
  }
67093
67133
  /**
67134
+ * Formats the span of one section in isolation and splices it back.
67135
+ *
67136
+ * The content is formatted on its own and reassembled with the same
67137
+ * surrounding newlines `updateSection` wrote, so the markers and every byte
67138
+ * outside them survive untouched.
67139
+ *
67140
+ * The span is formatted only while its markers still bound exactly the text
67141
+ * `updateSection` wrote. The markers are paired again here, after every
67142
+ * section has been written, and a marker another section wrote can win that
67143
+ * pairing; the text between such a pair is not this tool's to format.
67144
+ * @param {string} name - The name of the section.
67145
+ * @param {string} content - The content `updateSection` wrote, padded.
67146
+ */
67147
+ async formatSection(name, content) {
67148
+ const [startIndex, stopIndex] = this.getTokenIndexes(name);
67149
+ if (!startIndex || !stopIndex) return;
67150
+ if (startIndex > stopIndex || this.fileContent.slice(startIndex, stopIndex) !== layoutSpan(content, true)) {
67151
+ this.log.warn(`The '${name}' markers no longer bound the text written to them. Leaving the section unformatted`);
67152
+ return;
67153
+ }
67154
+ const formatted = content === "" ? "" : (await formatMarkdown(content)).trim();
67155
+ const span = formatted === "" ? "\n" : layoutSpan(formatted, true);
67156
+ this.fileContent = `${this.fileContent.slice(0, startIndex)}${span}${this.fileContent.slice(stopIndex)}`;
67157
+ }
67158
+ /**
67159
+ * Formats every span this editor replaced, one span at a time.
67160
+ *
67161
+ * Each span is located again before it is formatted, because formatting the
67162
+ * previous one moves the indexes of the spans after it. A formatted span no
67163
+ * longer holds the text `updateSection` wrote, so it is forgotten once
67164
+ * formatted.
67165
+ * @returns {Promise<void>}
67166
+ */
67167
+ async formatUpdatedSections() {
67168
+ for (const [name, content] of this.updatedSections) await this.formatSection(name, content);
67169
+ this.updatedSections.clear();
67170
+ }
67171
+ /**
67094
67172
  * Dumps the modified content back to the README file.
67095
- * @param {boolean} [prettier=true] - Run the result through prettier before
67096
- * writing. Callers pass the resolved `pretty` input; it defaults to true so
67097
- * constructing a ReadmeEditor directly keeps the formatting behaviour.
67173
+ * @param {boolean} [prettier=true] - Run the replaced spans through prettier
67174
+ * before writing. Callers pass the resolved `pretty` input; it defaults to
67175
+ * true so constructing a ReadmeEditor directly keeps the formatting
67176
+ * behaviour. Text outside the markers is never formatted, whatever this
67177
+ * flag says — see `docs/tool-contract.md`.
67098
67178
  * @returns {Promise<void>}
67099
67179
  */
67100
67180
  async dumpToFile(prettier = true) {
67101
- const content = prettier ? await formatMarkdown(this.fileContent) : this.fileContent;
67181
+ if (prettier) await this.formatUpdatedSections();
67182
+ const content = this.crlf ? this.fileContent.replaceAll("\n", "\r\n") : this.fileContent;
67102
67183
  if (process.env.GITHUB_ACTIONS) setOutput("readme_after", content);
67103
67184
  return fs$3.promises.writeFile(this.filePath, content, "utf8");
67104
67185
  }
package/dist/mjs/index.js CHANGED
@@ -10,7 +10,6 @@ import * as feather from "feather-icons";
10
10
  import { icons } from "feather-icons";
11
11
  import chalkPkg from "chalk";
12
12
  import { execFileSync, execSync } from "node:child_process";
13
- import { EOL } from "node:os";
14
13
  import * as markdown from "prettier/plugins/markdown";
15
14
  import * as yaml from "prettier/plugins/yaml";
16
15
  import { format } from "prettier/standalone";
@@ -860,14 +859,51 @@ const startTokenFormat = "(^|[^`\\\\])<!--\\s+start\\s+%s\\s+-->";
860
859
  * The format for the end token of a section.
861
860
  */
862
861
  const endTokenFormat = "(^|[^`\\\\])<!--\\s+end\\s+%s\\s+-->";
862
+ /**
863
+ * Lays out section content the way it sits between its markers.
864
+ * @param {string} content - The trimmed section content.
865
+ * @param {boolean} addNewlines - Whether to pad the content with newlines.
866
+ * @returns {string} - The text that goes between the markers.
867
+ */
868
+ function layoutSpan(content, addNewlines) {
869
+ return addNewlines ? `\n\n${content}\n` : content;
870
+ }
871
+ /**
872
+ * True when every line break in `text` is CRLF, and there is at least one.
873
+ *
874
+ * Only a document that is CRLF throughout is edited as CRLF. Removing the `\r`
875
+ * before each `\n` and adding it back is then an exact round trip, so the
876
+ * bytes outside the markers survive. A document that mixes the two is left as
877
+ * it is, since no single ending would reproduce it.
878
+ * @param {string} text - The document.
879
+ * @returns {boolean} - Whether the document uses CRLF line endings.
880
+ */
881
+ function usesCrlf(text) {
882
+ return text.includes("\r\n") && !/(^|[^\r])\n/.test(text);
883
+ }
863
884
  var ReadmeEditor = class {
864
885
  log = new LogTask("ReadmeEditor");
865
886
  /**
866
887
  * The path to the README file.
867
888
  */
868
889
  filePath;
890
+ /**
891
+ * The document with LF line endings, whatever the file uses. Every edit and
892
+ * every formatter pass works on LF; `dumpToFile` restores the file's own
893
+ * ending on the way out.
894
+ */
869
895
  fileContent;
870
896
  /**
897
+ * Whether the file on disk is CRLF throughout — see `usesCrlf`.
898
+ */
899
+ crlf = false;
900
+ /**
901
+ * The padded sections this editor has replaced, each against the content it
902
+ * wrote. `dumpToFile` formats these spans and nothing else — see
903
+ * `formatUpdatedSections`.
904
+ */
905
+ updatedSections = /* @__PURE__ */ new Map();
906
+ /**
871
907
  * Creates a new instance of `ReadmeEditor`.
872
908
  * @param {string} filePath - The path to the README file.
873
909
  */
@@ -875,15 +911,17 @@ var ReadmeEditor = class {
875
911
  this.filePath = filePath;
876
912
  try {
877
913
  fs.accessSync(filePath);
878
- this.fileContent = fs.readFileSync(filePath, "utf8");
879
- if (process.env.GITHUB_ACTIONS) core.setOutput("readme_before", this.fileContent);
914
+ const raw = fs.readFileSync(filePath, "utf8");
915
+ if (process.env.GITHUB_ACTIONS) core.setOutput("readme_before", raw);
916
+ this.crlf = usesCrlf(raw);
917
+ this.fileContent = this.crlf ? raw.replaceAll("\r\n", "\n") : raw;
880
918
  } catch (error) {
881
919
  this.log.fail(`Readme at '${filePath}' does not exist.`);
882
920
  throw error;
883
921
  }
884
922
  }
885
923
  /**
886
- * Gets the current README content.
924
+ * Gets the current README content, with LF line endings.
887
925
  * @returns {string} - The README file content.
888
926
  */
889
927
  getReadmeContent() {
@@ -918,24 +956,66 @@ var ReadmeEditor = class {
918
956
  */
919
957
  updateSection(name, providedContent, addNewlines = true) {
920
958
  const log = new LogTask(name);
921
- const content = (Array.isArray(providedContent) ? providedContent.join(EOL) : providedContent ?? "").trim();
959
+ const content = (Array.isArray(providedContent) ? providedContent.join("\n") : providedContent ?? "").replaceAll("\r\n", "\n").trim();
922
960
  log.info(`Looking for the ${name} token in ${this.filePath}`);
923
961
  const [startIndex, stopIndex] = this.getTokenIndexes(name, log);
924
962
  if (startIndex && stopIndex) {
925
963
  const beforeContent = this.fileContent.slice(0, startIndex);
926
964
  const afterContent = this.fileContent.slice(stopIndex);
927
- this.fileContent = addNewlines ? `${beforeContent}\n\n${content}\n${afterContent}` : `${beforeContent}${content}${afterContent}`;
965
+ this.fileContent = `${beforeContent}${layoutSpan(content, addNewlines)}${afterContent}`;
966
+ if (addNewlines) this.updatedSections.set(name, content);
928
967
  }
929
968
  }
930
969
  /**
970
+ * Formats the span of one section in isolation and splices it back.
971
+ *
972
+ * The content is formatted on its own and reassembled with the same
973
+ * surrounding newlines `updateSection` wrote, so the markers and every byte
974
+ * outside them survive untouched.
975
+ *
976
+ * The span is formatted only while its markers still bound exactly the text
977
+ * `updateSection` wrote. The markers are paired again here, after every
978
+ * section has been written, and a marker another section wrote can win that
979
+ * pairing; the text between such a pair is not this tool's to format.
980
+ * @param {string} name - The name of the section.
981
+ * @param {string} content - The content `updateSection` wrote, padded.
982
+ */
983
+ async formatSection(name, content) {
984
+ const [startIndex, stopIndex] = this.getTokenIndexes(name);
985
+ if (!startIndex || !stopIndex) return;
986
+ if (startIndex > stopIndex || this.fileContent.slice(startIndex, stopIndex) !== layoutSpan(content, true)) {
987
+ this.log.warn(`The '${name}' markers no longer bound the text written to them. Leaving the section unformatted`);
988
+ return;
989
+ }
990
+ const formatted = content === "" ? "" : (await formatMarkdown(content)).trim();
991
+ const span = formatted === "" ? "\n" : layoutSpan(formatted, true);
992
+ this.fileContent = `${this.fileContent.slice(0, startIndex)}${span}${this.fileContent.slice(stopIndex)}`;
993
+ }
994
+ /**
995
+ * Formats every span this editor replaced, one span at a time.
996
+ *
997
+ * Each span is located again before it is formatted, because formatting the
998
+ * previous one moves the indexes of the spans after it. A formatted span no
999
+ * longer holds the text `updateSection` wrote, so it is forgotten once
1000
+ * formatted.
1001
+ * @returns {Promise<void>}
1002
+ */
1003
+ async formatUpdatedSections() {
1004
+ for (const [name, content] of this.updatedSections) await this.formatSection(name, content);
1005
+ this.updatedSections.clear();
1006
+ }
1007
+ /**
931
1008
  * Dumps the modified content back to the README file.
932
- * @param {boolean} [prettier=true] - Run the result through prettier before
933
- * writing. Callers pass the resolved `pretty` input; it defaults to true so
934
- * constructing a ReadmeEditor directly keeps the formatting behaviour.
1009
+ * @param {boolean} [prettier=true] - Run the replaced spans through prettier
1010
+ * before writing. Callers pass the resolved `pretty` input; it defaults to
1011
+ * true so constructing a ReadmeEditor directly keeps the formatting
1012
+ * behaviour. Text outside the markers is never formatted, whatever this
1013
+ * flag says — see `docs/tool-contract.md`.
935
1014
  * @returns {Promise<void>}
936
1015
  */
937
1016
  async dumpToFile(prettier = true) {
938
- const content = prettier ? await formatMarkdown(this.fileContent) : this.fileContent;
1017
+ if (prettier) await this.formatUpdatedSections();
1018
+ const content = this.crlf ? this.fileContent.replaceAll("\n", "\r\n") : this.fileContent;
939
1019
  if (process.env.GITHUB_ACTIONS) core.setOutput("readme_after", content);
940
1020
  return fs.promises.writeFile(this.filePath, content, "utf8");
941
1021
  }
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.9",
4
+ "version": "2.0.0",
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",
@@ -128,7 +128,7 @@
128
128
  "@semantic-release/github",
129
129
  "@semantic-release/git"
130
130
  ],
131
- "preset": "angular"
131
+ "preset": "conventionalcommits"
132
132
  },
133
133
  "dependencies": {
134
134
  "@actions/core": "^3.0.0",
@@ -156,6 +156,7 @@
156
156
  "@vitest/coverage-v8": "4.1.11",
157
157
  "@voidzero-dev/vite-plus-core": "0.2.8",
158
158
  "commitizen": "^4.3.1",
159
+ "conventional-changelog-conventionalcommits": "^9.3.1",
159
160
  "conventional-commits": "^1.6.0",
160
161
  "cz-conventional-changelog": "^3.3.0",
161
162
  "dotenv": "^17.2.4",