fork-version 5.0.2 → 5.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # Fork Version
2
2
 
3
+ ## [5.1.1](https://github.com/eglavin/fork-version/compare/v5.1.0...v5.1.1) (2026-04-12)
4
+
5
+
6
+ ### Docs
7
+
8
+ * add all files and custom file manager examples ([43c85a4](https://github.com/eglavin/fork-version/commit/43c85a482906f73c7b45a28a522bec150b3f2cff))
9
+ * update build metadata info in readme ([e03c362](https://github.com/eglavin/fork-version/commit/e03c362094b7f0685fd386c7754f705a4d727472))
10
+
11
+
12
+ ### Refactor
13
+
14
+ * move build metadata out of yaml class ([59627fc](https://github.com/eglavin/fork-version/commit/59627fc903bcd5aae1b44954fd6183417025a2f3))
15
+
16
+
17
+ ## [5.1.0](https://github.com/eglavin/fork-version/compare/v5.0.2...v5.1.0) (2026-04-11)
18
+
19
+
20
+ ### Features
21
+
22
+ * implement custom user provided file managers ([9c6ed7c](https://github.com/eglavin/fork-version/commit/9c6ed7ccf39c736dfdb53b959f989c4139892523))
23
+
24
+
25
+ ### Docs
26
+
27
+ * update readme with details on using custom file managers ([57a6f17](https://github.com/eglavin/fork-version/commit/57a6f174473992b113c47acee53f24a82db4e56d))
28
+
29
+
30
+ ### Refactor
31
+
32
+ * remove name property from file managers ([202fc21](https://github.com/eglavin/fork-version/commit/202fc21c49f13081d352f2e5c549a8ea3c68de7f))
33
+ * use loose object when validating custom file managers ([3b3f8a9](https://github.com/eglavin/fork-version/commit/3b3f8a92d082d9374f6fba8acd39655cc7405e39))
34
+
35
+
36
+ ### Test
37
+
38
+ * add custom file manager object test ([6b58f40](https://github.com/eglavin/fork-version/commit/6b58f404ce6c4059ab43dfe2ccc3a189ae098f65))
39
+
40
+
3
41
  ## [5.0.2](https://github.com/eglavin/fork-version/compare/v5.0.1...v5.0.2) (2026-04-10)
4
42
 
5
43
 
package/README.md CHANGED
@@ -292,36 +292,37 @@ Alternatively you can define your config using a key in your `package.json` file
292
292
 
293
293
  #### Config Properties
294
294
 
295
- | Property | Type | Default | Description |
296
- | :---------------------------------------------------- | :--------------- | :---------------------- | :------------------------------------------------------------------------------------------------------------------ |
297
- | command | string | `main` | The command to run. Can be one of: main, inspect-version, inspect-tag, validate-config. Defaults to main. |
298
- | [files](#configfiles) | Array\<string> | `["package.json", ...]` | List of the files to be updated |
299
- | [glob](#configglob) | string | - | Glob pattern to match files to be updated |
300
- | path | string | `process.cwd()` | The path Fork-Version will run from |
301
- | changelog | string | `CHANGELOG.md` | Name of the changelog file |
302
- | header | string | `# Changelog...` | The header text for the changelog |
303
- | [tagPrefix](#configtagprefix) | string | `v` | Prefix for the created tag |
304
- | [preRelease](#configprerelease) | string / boolean | - | Make a pre-release with optional label if given value is a string |
305
- | currentVersion | string | - | Use this version instead of trying to determine one |
306
- | nextVersion | string | - | Attempt to update to this version, instead of incrementing using "conventional-commit" |
307
- | [releaseAs](#configreleaseas) | string | - | Release as increments the version by the specified level. Overrides the default behaviour of "conventional-commit". |
308
- | allowMultipleVersions | boolean | true | Don't throw an error if multiple versions are found in the given files. |
309
- | commitAll | boolean | false | Commit all changes, not just files updated by Fork-Version |
310
- | changelogAll | boolean | false | If this flag is set, all default commit types will be added to the changelog, not just `feat` and `fix`. |
311
- | debug | boolean | false | Output debug information |
312
- | dryRun | boolean | false | No output will be written to disk or committed |
313
- | silent | boolean | false | Run without logging to the terminal |
314
- | gitTagFallback | boolean | true | If unable to find a version in the given files, fallback and attempt to use the latest git tag |
315
- | sign | boolean | false | Sign the commit with the systems GPG key |
316
- | verify | boolean | false | Run user defined git hooks before committing |
317
- | asJson | boolean | false | Print inspected output as a parsable json string |
318
- | skipBump | boolean | false | Skip the bump step |
319
- | skipChangelog | boolean | false | Skip the changelog step |
320
- | skipCommit | boolean | false | Skip the commit step |
321
- | skipTag | boolean | false | Skip the tag step |
322
- | [changelogPresetConfig](#configchangelogpresetconfig) | object | {} | Override defaults from the "conventional-changelog-conventionalcommits" preset configuration |
323
- | releaseMessageSuffix | string | - | Add a suffix to the end of the release message |
324
- | [commitParserOptions](#configcommitparseroptions) | object | {} | Options to pass to commits parser |
295
+ | Property | Type | Default | Description |
296
+ | :---------------------------------------------------- | :------------------- | :---------------------- | :------------------------------------------------------------------------------------------------------------------ |
297
+ | command | string | `main` | The command to run. Can be one of: main, inspect-version, inspect-tag, validate-config. Defaults to main. |
298
+ | [files](#configfiles) | Array\<string> | `["package.json", ...]` | List of the files to be updated |
299
+ | [customFileManagers](#custom-file-updaters) | Array\<IFileManager> | - | Support for user provided custom file managers |
300
+ | [glob](#configglob) | string | - | Glob pattern to match files to be updated |
301
+ | path | string | `process.cwd()` | The path Fork-Version will run from |
302
+ | changelog | string | `CHANGELOG.md` | Name of the changelog file |
303
+ | header | string | `# Changelog...` | The header text for the changelog |
304
+ | [tagPrefix](#configtagprefix) | string | `v` | Prefix for the created tag |
305
+ | [preRelease](#configprerelease) | string / boolean | - | Make a pre-release with optional label if given value is a string |
306
+ | currentVersion | string | - | Use this version instead of trying to determine one |
307
+ | nextVersion | string | - | Attempt to update to this version, instead of incrementing using "conventional-commit" |
308
+ | [releaseAs](#configreleaseas) | string | - | Release as increments the version by the specified level. Overrides the default behaviour of "conventional-commit". |
309
+ | allowMultipleVersions | boolean | true | Don't throw an error if multiple versions are found in the given files. |
310
+ | commitAll | boolean | false | Commit all changes, not just files updated by Fork-Version |
311
+ | changelogAll | boolean | false | If this flag is set, all default commit types will be added to the changelog, not just `feat` and `fix`. |
312
+ | debug | boolean | false | Output debug information |
313
+ | dryRun | boolean | false | No output will be written to disk or committed |
314
+ | silent | boolean | false | Run without logging to the terminal |
315
+ | gitTagFallback | boolean | true | If unable to find a version in the given files, fallback and attempt to use the latest git tag |
316
+ | sign | boolean | false | Sign the commit with the systems GPG key |
317
+ | verify | boolean | false | Run user defined git hooks before committing |
318
+ | asJson | boolean | false | Print inspected output as a parsable json string |
319
+ | skipBump | boolean | false | Skip the bump step |
320
+ | skipChangelog | boolean | false | Skip the changelog step |
321
+ | skipCommit | boolean | false | Skip the commit step |
322
+ | skipTag | boolean | false | Skip the tag step |
323
+ | [changelogPresetConfig](#configchangelogpresetconfig) | object | {} | Override defaults from the "conventional-changelog-conventionalcommits" preset configuration |
324
+ | releaseMessageSuffix | string | - | Add a suffix to the end of the release message |
325
+ | [commitParserOptions](#configcommitparseroptions) | object | {} | Options to pass to commits parser |
325
326
 
326
327
  ##### config.files
327
328
 
@@ -337,7 +338,7 @@ By default Fork-Version will attempt to read versions from and update these file
337
338
  - "manifest.json"
338
339
  - "bower.json"
339
340
 
340
- See the [Supported File Types](#supported-file-types) section below to see the currently supported file types.
341
+ See the [Supported File Types](#supported-file-types) section below to see the currently supported file types and the [Custom File Updater's](#custom-file-updaters) section below to see how to support other file types.
341
342
 
342
343
  ##### config.glob
343
344
 
@@ -469,6 +470,16 @@ If you are using one of the following Git hosts, Fork-Version will automatically
469
470
  - [MS Build](#ms-build)
470
471
  - [ARM Bicep](#arm-bicep)
471
472
  - [Install Shield ISM](#install-shield-ism)
473
+ - [Custom File Updater's](#custom-file-updaters)
474
+
475
+ > [!Note]
476
+ > If your version strings include build metadata like one of the following examples:
477
+ >
478
+ > - 1.2.3+49a3f2b
479
+ > - 1.2.3-0+49a3f2b
480
+ > - 1.2.3-alpha.0+49a3f2b
481
+ >
482
+ > this metadata will be retained without modification.
472
483
 
473
484
  #### Json Package
474
485
 
@@ -493,9 +504,6 @@ publish_to: 'none'
493
504
  version: 1.2.3
494
505
  ```
495
506
 
496
- > [!NOTE]
497
- > If you're using Fork-Version for a flutter project, Fork-Version will split the version and the builder number on the "+" character, the version will be updated and the builder number will be left as is.
498
-
499
507
  #### Plain Text
500
508
 
501
509
  A plain text file is a file which contains just the version as the content. Files that end with `version.txt` will be treated as a plain text version file.
@@ -546,7 +554,69 @@ An Install Shield `*.ism` file can be either binary or an xml file. Fork-Version
546
554
 
547
555
  #### Custom File Updater's
548
556
 
549
- `TODO` [add support for custom file readers and writers through config #5](https://github.com/eglavin/fork-version/issues/5)
557
+ ***Released in version 5.1.0***
558
+
559
+ If you have a file type that isn't supported by default, you can create a custom file manager to read and write the updated version to that file.
560
+
561
+ To do this you will need to create a class or an object that implements the `IFileManager` interface and add an instance of that class or object to the `customFileManagers` array in your config.
562
+
563
+ The following example show a custom file manager for a json file with the name of `test.json` with the following structure:
564
+
565
+ Example `test.json` file:
566
+
567
+ ```json
568
+ {
569
+ "package": {
570
+ "version": "1.2.3"
571
+ }
572
+ }
573
+ ```
574
+
575
+ Example Custom File Manager implementation:
576
+
577
+ ```ts
578
+ // fork.config.ts
579
+ import { readFile, writeFile } from "node:fs/promises";
580
+ import { defineConfig, MissingPropertyException, type FileState, type IFileManager } from "fork-version";
581
+
582
+ class CustomFileManager implements IFileManager {
583
+ async read(filePath: string): Promise<FileState | undefined> {
584
+ const fileContent = await readFile(filePath, "utf-8");
585
+ if (fileContent) {
586
+ const parsedContent = JSON.parse(fileContent);
587
+ if ("package" in parsedContent && "version" in parsedContent.package) {
588
+ return {
589
+ path: filePath,
590
+ version: parsedContent.package.version,
591
+ };
592
+ }
593
+ }
594
+ throw new MissingPropertyException("My Custom File", "package.version");
595
+ }
596
+
597
+ async write(fileState: FileState, newVersion: string): Promise<void> {
598
+ const fileContent = await readFile(fileState.path, "utf-8");
599
+ if (fileContent) {
600
+ const parsedContent = JSON.parse(fileContent);
601
+ if ("package" in parsedContent && "version" in parsedContent.package) {
602
+ parsedContent.package.version = newVersion;
603
+ const updatedContent = JSON.stringify(parsedContent, null, 2);
604
+ await writeFile(fileState.path, updatedContent, "utf-8");
605
+ }
606
+ }
607
+ }
608
+
609
+ isSupportedFile(fileName: string) {
610
+ return fileName === "test.json";
611
+ }
612
+ }
613
+
614
+ export default defineConfig({
615
+ customFileManagers: [new CustomFileManager()],
616
+ });
617
+ ```
618
+
619
+ > [See `IFileManager` interface to see the required methods and properties for a custom file manager.](./src/files/file-manager.ts)
550
620
 
551
621
  ### Code Usage
552
622
 
package/dist/cli.js CHANGED
@@ -30,7 +30,7 @@ async function runFork() {
30
30
  const result = await main(config, logger, fileManager, git);
31
31
  const branchName = await git.getBranchName();
32
32
  logger.log(`\nRun \`git push --follow-tags origin ${branchName}\` to push the changes and the tag.`);
33
- if (result.current.files.some((file) => file.name === "package.json" && !file.isPrivate)) {
33
+ if (result.current.files.some((file) => file.path.endsWith("package.json") && !file.isPrivate)) {
34
34
  const npmTag = typeof config.preRelease === "string" ? config.preRelease : "prerelease";
35
35
  logger.log(`${result.next.releaseType}`.startsWith("pre") ? `Run \`npm publish --tag ${npmTag}\` to publish the package.` : "Run `npm publish` to publish the package.");
36
36
  }
@@ -1,6 +1,6 @@
1
- import { ForkConfig } from "../config/types.js";
2
1
  import { Logger } from "../services/logger.js";
3
2
  import { FileManager } from "../files/file-manager.js";
3
+ import { ForkConfig } from "../config/types.js";
4
4
  import { Git } from "../services/git.js";
5
5
 
6
6
  //#region src/commands/inspect.d.ts
@@ -1,6 +1,6 @@
1
- import { ForkConfig } from "../config/types.js";
2
1
  import { Logger } from "../services/logger.js";
3
2
  import { FileManager } from "../files/file-manager.js";
3
+ import { ForkConfig } from "../config/types.js";
4
4
  import { Git } from "../services/git.js";
5
5
  import { CommitsSinceTag } from "../process/get-commits.js";
6
6
  import { CurrentVersion } from "../process/get-current-version.js";
@@ -14,7 +14,7 @@ async function main(config, logger, fileManager, git) {
14
14
  logger.log("Updating files: ");
15
15
  for (const outFile of current.files) {
16
16
  logger.log(` - ${outFile.path}`);
17
- fileManager.write(outFile, next.version);
17
+ await fileManager.write(outFile, next.version);
18
18
  }
19
19
  await updateChangelog(config, logger, next.version);
20
20
  await commitChanges(config, logger, git, current.files, next.version);
@@ -1,6 +1,6 @@
1
- import { ForkConfigSchema } from "./schema.js";
1
+ import { ForkConfigJSONSchema, ForkConfigJSSchema } from "./schema.js";
2
2
  import { parse } from "node:path";
3
- import { readFileSync } from "node:fs";
3
+ import { readFile } from "node:fs/promises";
4
4
  import JoyCon from "joycon";
5
5
  import { bundleRequire } from "bundle-require";
6
6
  //#region src/config/load-config.ts
@@ -23,21 +23,21 @@ async function loadConfigFile(cwd) {
23
23
  ]);
24
24
  if (!configFilePath) return {};
25
25
  if (configFilePath.endsWith("json")) {
26
- const fileContent = JSON.parse(readFileSync(configFilePath).toString());
26
+ const fileContent = JSON.parse(await readFile(configFilePath, "utf8"));
27
27
  if (configFilePath.endsWith("package.json")) {
28
28
  if (fileContent[PACKAGE_JSON_CONFIG_KEY] && typeof fileContent[PACKAGE_JSON_CONFIG_KEY] === "object") {
29
- const parsed = ForkConfigSchema.partial().safeParse(fileContent[PACKAGE_JSON_CONFIG_KEY]);
29
+ const parsed = ForkConfigJSONSchema.partial().safeParse(fileContent[PACKAGE_JSON_CONFIG_KEY]);
30
30
  if (!parsed.success) throw new Error(`Validation error in: ${configFilePath}`, { cause: parsed.error });
31
31
  return parsed.data;
32
32
  }
33
33
  return {};
34
34
  }
35
- const parsed = ForkConfigSchema.partial().safeParse(fileContent);
35
+ const parsed = ForkConfigJSONSchema.partial().safeParse(fileContent);
36
36
  if (!parsed.success) throw new Error(`Validation error in: ${configFilePath}`, { cause: parsed.error });
37
37
  return parsed.data;
38
38
  }
39
39
  const fileContent = await bundleRequire({ filepath: configFilePath });
40
- const parsed = ForkConfigSchema.partial().safeParse(fileContent.mod.default || fileContent.mod);
40
+ const parsed = ForkConfigJSSchema.partial().safeParse(fileContent.mod.default || fileContent.mod);
41
41
  if (!parsed.success) throw new Error(`Validation error in: ${configFilePath}`, { cause: parsed.error });
42
42
  return parsed.data;
43
43
  }
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
 
3
3
  //#region src/config/schema.d.ts
4
- declare const ForkConfigSchema: z.ZodObject<{
4
+ declare const ForkConfigJSONSchema: z.ZodObject<{
5
5
  command: z.ZodLiteral<"main" | "inspect" | "inspect-version" | "inspect-tag" | "validate-config">;
6
6
  inspectVersion: z.ZodOptional<z.ZodBoolean>;
7
7
  files: z.ZodArray<z.ZodString>;
@@ -46,5 +46,55 @@ declare const ForkConfigSchema: z.ZodObject<{
46
46
  releaseMessageSuffix: z.ZodOptional<z.ZodString>;
47
47
  commitParserOptions: z.ZodOptional<z.ZodObject<{}, z.core.$loose>>;
48
48
  }, z.core.$strip>;
49
+ declare const ForkConfigJSSchema: z.ZodObject<{
50
+ command: z.ZodOptional<z.ZodLiteral<"main" | "inspect" | "inspect-version" | "inspect-tag" | "validate-config">>;
51
+ inspectVersion: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
52
+ files: z.ZodOptional<z.ZodArray<z.ZodString>>;
53
+ glob: z.ZodOptional<z.ZodOptional<z.ZodString>>;
54
+ path: z.ZodOptional<z.ZodString>;
55
+ changelog: z.ZodOptional<z.ZodString>;
56
+ header: z.ZodOptional<z.ZodString>;
57
+ tagPrefix: z.ZodOptional<z.ZodString>;
58
+ preRelease: z.ZodOptional<z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodBoolean]>>>;
59
+ currentVersion: z.ZodOptional<z.ZodOptional<z.ZodString>>;
60
+ nextVersion: z.ZodOptional<z.ZodOptional<z.ZodString>>;
61
+ releaseAs: z.ZodOptional<z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"major">, z.ZodLiteral<"minor">, z.ZodLiteral<"patch">]>>>;
62
+ allowMultipleVersions: z.ZodOptional<z.ZodBoolean>;
63
+ commitAll: z.ZodOptional<z.ZodBoolean>;
64
+ changelogAll: z.ZodOptional<z.ZodBoolean>;
65
+ debug: z.ZodOptional<z.ZodBoolean>;
66
+ dryRun: z.ZodOptional<z.ZodBoolean>;
67
+ silent: z.ZodOptional<z.ZodBoolean>;
68
+ gitTagFallback: z.ZodOptional<z.ZodBoolean>;
69
+ sign: z.ZodOptional<z.ZodBoolean>;
70
+ verify: z.ZodOptional<z.ZodBoolean>;
71
+ asJson: z.ZodOptional<z.ZodBoolean>;
72
+ skipBump: z.ZodOptional<z.ZodBoolean>;
73
+ skipChangelog: z.ZodOptional<z.ZodBoolean>;
74
+ skipCommit: z.ZodOptional<z.ZodBoolean>;
75
+ skipTag: z.ZodOptional<z.ZodBoolean>;
76
+ detectedGitHost: z.ZodOptional<z.ZodOptional<z.ZodString>>;
77
+ changelogPresetConfig: z.ZodOptional<z.ZodOptional<z.ZodObject<{
78
+ types: z.ZodOptional<z.ZodArray<z.ZodObject<{
79
+ type: z.ZodString;
80
+ scope: z.ZodOptional<z.ZodString>;
81
+ section: z.ZodOptional<z.ZodString>;
82
+ hidden: z.ZodOptional<z.ZodBoolean>;
83
+ }, z.core.$strip>>>;
84
+ commitUrlFormat: z.ZodOptional<z.ZodString>;
85
+ compareUrlFormat: z.ZodOptional<z.ZodString>;
86
+ issueUrlFormat: z.ZodOptional<z.ZodString>;
87
+ userUrlFormat: z.ZodOptional<z.ZodString>;
88
+ releaseCommitMessageFormat: z.ZodOptional<z.ZodString>;
89
+ issuePrefixes: z.ZodOptional<z.ZodArray<z.ZodString>>;
90
+ }, z.core.$strip>>>;
91
+ releaseMessageSuffix: z.ZodOptional<z.ZodOptional<z.ZodString>>;
92
+ commitParserOptions: z.ZodOptional<z.ZodOptional<z.ZodObject<{}, z.core.$loose>>>;
93
+ customFileManagers: z.ZodArray<z.ZodObject<{
94
+ read: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
95
+ write: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
96
+ isSupportedFile: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
97
+ }, z.core.$loose>>;
98
+ }, z.core.$strip>;
49
99
  //#endregion
50
- export { ForkConfigSchema };
100
+ export { ForkConfigJSONSchema, ForkConfigJSSchema };
@@ -15,7 +15,7 @@ const ChangelogPresetConfigSchema = z.object({
15
15
  releaseCommitMessageFormat: z.string().describe("A string to be used to format the auto-generated release commit message."),
16
16
  issuePrefixes: z.array(z.string()).describe("List of prefixes used to detect references to issues.")
17
17
  });
18
- const ForkConfigSchema = z.object({
18
+ const ForkConfigJSONSchema = z.object({
19
19
  command: z.literal([
20
20
  "main",
21
21
  "inspect",
@@ -57,5 +57,11 @@ const ForkConfigSchema = z.object({
57
57
  releaseMessageSuffix: z.string().optional().describe("Add a suffix to the release commit message."),
58
58
  commitParserOptions: z.looseObject({}).optional().describe("Options to pass to commits parser.")
59
59
  });
60
+ const CustomFileManagerSchema = z.looseObject({
61
+ read: z.function(),
62
+ write: z.function(),
63
+ isSupportedFile: z.function()
64
+ });
65
+ const ForkConfigJSSchema = ForkConfigJSONSchema.partial().extend({ customFileManagers: z.array(CustomFileManagerSchema).describe("List of custom file managers to use. See documentation for details.") });
60
66
  //#endregion
61
- export { ChangelogPresetConfigSchema, ChangelogPresetConfigTypeSchema, ForkConfigSchema };
67
+ export { ChangelogPresetConfigSchema, ChangelogPresetConfigTypeSchema, ForkConfigJSONSchema, ForkConfigJSSchema };
@@ -1,5 +1,6 @@
1
1
  import { getCliArguments } from "./cli-arguments.js";
2
2
  import { ParserOptions } from "../commit-parser/options.js";
3
+ import { IFileManager } from "../files/file-manager.js";
3
4
 
4
5
  //#region src/config/types.d.ts
5
6
  interface ChangelogPresetConfigType {
@@ -87,6 +88,11 @@ interface ForkConfig {
87
88
  * ```
88
89
  */
89
90
  files: string[];
91
+ /**
92
+ * List of custom file managers to use. See documentation for details.
93
+ * @default undefined
94
+ */
95
+ customFileManagers?: IFileManager[];
90
96
  /**
91
97
  * Glob pattern to match files to be updated.
92
98
  *
@@ -1,6 +1,5 @@
1
1
  import { MissingPropertyException } from "./file-manager.js";
2
- import { basename } from "node:path";
3
- import { readFileSync, writeFileSync } from "node:fs";
2
+ import { readFile, writeFile } from "node:fs/promises";
4
3
  //#region src/files/arm-bicep.ts
5
4
  /**
6
5
  * An ARM bicep file with metadata and variable called contentVersion.
@@ -16,19 +15,18 @@ var ARMBicep = class {
16
15
  #metadataRegex = /(metadata contentVersion *= *['"])(?<version>[^'"]+)(['"])/;
17
16
  /** https://regex101.com/r/iKCTF9/1 */
18
17
  #varRegex = /(var contentVersion(?: string)? *= *['"])(?<version>[^'"]+)(['"])/;
19
- read(filePath) {
20
- const fileContents = readFileSync(filePath, "utf8");
18
+ async read(filePath) {
19
+ const fileContents = await readFile(filePath, "utf8");
21
20
  const metadataMatch = this.#metadataRegex.exec(fileContents);
22
21
  if (metadataMatch?.groups?.version) return {
23
- name: basename(filePath),
24
22
  path: filePath,
25
23
  version: metadataMatch.groups.version
26
24
  };
27
25
  throw new MissingPropertyException("ARM Bicep", "metadata contentVersion");
28
26
  }
29
- write(fileState, newVersion) {
30
- const updatedContent = readFileSync(fileState.path, "utf8").replace(this.#metadataRegex, `$1${newVersion}$3`).replace(this.#varRegex, `$1${newVersion}$3`);
31
- writeFileSync(fileState.path, updatedContent, "utf8");
27
+ async write(fileState, newVersion) {
28
+ const updatedContent = (await readFile(fileState.path, "utf8")).replace(this.#metadataRegex, `$1${newVersion}$3`).replace(this.#varRegex, `$1${newVersion}$3`);
29
+ await writeFile(fileState.path, updatedContent, "utf8");
32
30
  }
33
31
  isSupportedFile(fileName) {
34
32
  return fileName.endsWith(".bicep");
@@ -1,5 +1,5 @@
1
- import { ForkConfig } from "../config/types.js";
2
1
  import { Logger } from "../services/logger.js";
2
+ import { ForkConfig } from "../config/types.js";
3
3
 
4
4
  //#region src/files/file-manager.d.ts
5
5
  /**
@@ -12,45 +12,82 @@ declare class MissingPropertyException extends Error {
12
12
  constructor(fileType: string, propertyName: string);
13
13
  }
14
14
  interface FileState {
15
- name: string;
16
15
  path: string;
17
16
  version: string;
17
+ buildMetadata?: string;
18
18
  [other: string]: unknown;
19
19
  }
20
20
  interface IFileManager {
21
- read(fileName: string): FileState | undefined;
22
- write(fileState: FileState, newVersion: string): void;
23
- isSupportedFile(fileName: string): boolean;
21
+ /**
22
+ * Function to read the file and return its current state.
23
+ * @param filePath The path of the file to read, can be either an absolute path or a file name relative to the config path.
24
+ * @returns The state of the file, including its current version and any other relevant information.
25
+ * @throws {MissingPropertyException} If the file is missing a required property.
26
+ * @throws {Error} If an unexpected error occurs while reading the file.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * const fileState = await fileManager.read("package.json");
31
+ *
32
+ * // Returns
33
+ * { path: "package.json", version: "1.2.3" }
34
+ * ```
35
+ */
36
+ read(filePath: string): Promise<FileState | undefined>;
37
+ /**
38
+ * Function to write the new version to the file.
39
+ * @param fileState The current state of the file, including its path and current version.
40
+ * @param newVersion The new version string to write to the file.
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * await fileManager.write(
45
+ * { path: "package.json", version: "1.2.2" },
46
+ * "1.2.3"
47
+ * );
48
+ * ```
49
+ */
50
+ write(fileState: FileState, newVersion: string): Promise<void>;
51
+ /**
52
+ * Determine if the file manager supports the given file based on its name or path.
53
+ * File name will be transformed to lower case before checking for support to allow for case-insensitive file matching.
54
+ * @param filePath The name or path of the file to check.
55
+ * @returns `true` if the file is supported by this file manager, `false` otherwise.
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * fileManager.isSupportedFile("package.json");
60
+ * ```
61
+ */
62
+ isSupportedFile(filePath: string): boolean;
24
63
  }
25
64
  declare class FileManager {
26
65
  #private;
27
66
  constructor(config: ForkConfig, logger: Logger);
28
67
  /**
29
- * Get the state from the given file name.
68
+ * Get the state from the given file.
30
69
  *
31
70
  * @example
32
71
  * ```ts
33
72
  * fileManager.read("package.json");
34
- * ```
35
73
  *
36
- * @returns
37
- * ```json
38
- * { "name": "package.json", "path": "/path/to/package.json", "version": "1.2.3", "isPrivate": true }
74
+ * // Returns
75
+ * { path: "package.json", version: "1.2.3" }
39
76
  * ```
40
77
  */
41
- read(pathOrName: string): FileState | undefined;
78
+ read(pathOrName: string): Promise<FileState | undefined>;
42
79
  /**
43
- * Write the new version to the given file.
80
+ * Write new version to the given file.
44
81
  *
45
82
  * @example
46
83
  * ```ts
47
84
  * fileManager.write(
48
- * { name: "package.json", path: "/path/to/package.json", version: "1.2.2" },
85
+ * { path: "package.json", version: "1.2.2" },
49
86
  * "1.2.3"
50
87
  * );
51
88
  * ```
52
89
  */
53
- write(fileState: FileState, newVersion: string): void;
90
+ write(fileState: FileState, newVersion: string): Promise<void>;
54
91
  }
55
92
  //#endregion
56
93
  export { FileManager, FileState, IFileManager, MissingPropertyException };
@@ -5,7 +5,8 @@ import { PlainText } from "./plain-text.js";
5
5
  import { MSBuildProject } from "./ms-build-project.js";
6
6
  import { ARMBicep } from "./arm-bicep.js";
7
7
  import { InstallShieldISM } from "./install-shield-ism.js";
8
- import { basename, isAbsolute, resolve } from "node:path";
8
+ import { extractBuildMetadata } from "../utils/extract-build-metadata.js";
9
+ import { isAbsolute, relative, resolve } from "node:path";
9
10
  //#region src/files/file-manager.ts
10
11
  /**
11
12
  * Exception thrown if a file manager encounters a file missing a required property,
@@ -28,7 +29,7 @@ var FileManager = class {
28
29
  constructor(config, logger) {
29
30
  this.#config = config;
30
31
  this.#logger = logger;
31
- this.#fileManagers = [
32
+ const builtinFileManagers = [
32
33
  new JSONPackage(),
33
34
  new YAMLPackage(),
34
35
  new PlainText(),
@@ -36,51 +37,63 @@ var FileManager = class {
36
37
  new ARMBicep(),
37
38
  new InstallShieldISM()
38
39
  ];
40
+ if (config.customFileManagers) this.#fileManagers = [...config.customFileManagers, ...builtinFileManagers];
41
+ else this.#fileManagers = builtinFileManagers;
39
42
  }
40
43
  /**
41
- * Get the state from the given file name.
44
+ * Get the state from the given file.
42
45
  *
43
46
  * @example
44
47
  * ```ts
45
48
  * fileManager.read("package.json");
46
- * ```
47
49
  *
48
- * @returns
49
- * ```json
50
- * { "name": "package.json", "path": "/path/to/package.json", "version": "1.2.3", "isPrivate": true }
50
+ * // Returns
51
+ * { path: "package.json", version: "1.2.3" }
51
52
  * ```
52
53
  */
53
- read(pathOrName) {
54
- const _fileName = pathOrName.toLowerCase();
54
+ async read(pathOrName) {
55
55
  const filePath = isAbsolute(pathOrName) ? pathOrName : resolve(this.#config.path, pathOrName);
56
+ const relativePath = relative(this.#config.path, filePath);
57
+ const fileNameLower = relativePath.toLocaleLowerCase();
56
58
  if (!fileExists(filePath)) return;
57
- for (const fileManager of this.#fileManagers) if (fileManager.isSupportedFile(_fileName)) {
59
+ for (const fileManager of this.#fileManagers) if (fileManager.isSupportedFile(fileNameLower)) {
58
60
  try {
59
- return fileManager.read(filePath);
61
+ const fileState = await fileManager.read(filePath);
62
+ if (fileState) {
63
+ const { version, buildMetadata } = extractBuildMetadata(fileState.version);
64
+ if (buildMetadata) {
65
+ fileState.version = version;
66
+ fileState.buildMetadata = buildMetadata;
67
+ }
68
+ }
69
+ return fileState;
60
70
  } catch (error) {
61
- if (error instanceof MissingPropertyException) this.#logger.warn(`[File Manager] Missing '${error.propertyName}' property in ${error.fileType} file: ${basename(_fileName)}`);
71
+ if (error instanceof MissingPropertyException) this.#logger.warn(`[File Manager] Missing '${error.propertyName}' property in ${error.fileType} file: ${relativePath}`);
62
72
  else throw new Error(`An unexpected error occurred while reading file: ${filePath}`, { cause: error });
63
73
  }
64
74
  return;
65
75
  }
66
- this.#logger.error(`[File Manager] Unsupported file: ${pathOrName}`);
76
+ this.#logger.error(`[File Manager] Unsupported file: ${relativePath}`);
67
77
  }
68
78
  /**
69
- * Write the new version to the given file.
79
+ * Write new version to the given file.
70
80
  *
71
81
  * @example
72
82
  * ```ts
73
83
  * fileManager.write(
74
- * { name: "package.json", path: "/path/to/package.json", version: "1.2.2" },
84
+ * { path: "package.json", version: "1.2.2" },
75
85
  * "1.2.3"
76
86
  * );
77
87
  * ```
78
88
  */
79
- write(fileState, newVersion) {
89
+ async write(fileState, newVersion) {
80
90
  if (this.#config.dryRun) return;
81
- const _fileName = fileState.name.toLowerCase();
82
- for (const fileManager of this.#fileManagers) if (fileManager.isSupportedFile(_fileName)) return fileManager.write(fileState, newVersion);
83
- this.#logger.error(`[File Manager] Unsupported file: ${fileState.path}`);
91
+ const relativePath = relative(this.#config.path, fileState.path);
92
+ const fileNameLower = relativePath.toLocaleLowerCase();
93
+ let updatedVersion = newVersion;
94
+ if (fileState?.buildMetadata) updatedVersion += `+${fileState.buildMetadata}`;
95
+ for (const fileManager of this.#fileManagers) if (fileManager.isSupportedFile(fileNameLower)) return await fileManager.write(fileState, updatedVersion);
96
+ this.#logger.error(`[File Manager] Unsupported file: ${relativePath}`);
84
97
  }
85
98
  };
86
99
  //#endregion
@@ -1,6 +1,5 @@
1
1
  import { MissingPropertyException } from "./file-manager.js";
2
- import { basename } from "node:path";
3
- import { readFileSync, writeFileSync } from "node:fs";
2
+ import { readFile, writeFile } from "node:fs/promises";
4
3
  import * as cheerio from "cheerio/slim";
5
4
  //#region src/files/install-shield-ism.ts
6
5
  /**
@@ -29,23 +28,22 @@ var InstallShieldISM = class {
29
28
  xmlMode: true,
30
29
  xml: { decodeEntities: false }
31
30
  };
32
- read(filePath) {
33
- const fileContents = readFileSync(filePath, "utf8");
31
+ async read(filePath) {
32
+ const fileContents = await readFile(filePath, "utf8");
34
33
  const version = cheerio.load(fileContents, this.#cheerioOptions)("msi > table[name=\"Property\"] > row > td:contains(\"ProductVersion\")").next().text().trim();
35
34
  if (version) return {
36
- name: basename(filePath),
37
35
  path: filePath,
38
36
  version
39
37
  };
40
38
  throw new MissingPropertyException("InstallShield ISM", "ProductVersion");
41
39
  }
42
- write(fileState, newVersion) {
43
- const fileContents = readFileSync(fileState.path, "utf8");
40
+ async write(fileState, newVersion) {
41
+ const fileContents = await readFile(fileState.path, "utf8");
44
42
  const $ = cheerio.load(fileContents, this.#cheerioOptions);
45
43
  const versionCell = $("msi > table[name=\"Property\"] > row > td:contains(\"ProductVersion\")").next();
46
44
  if (versionCell.length > 0) versionCell.text(newVersion);
47
45
  const updatedContent = $.xml().replaceAll("\"/>", "\" />");
48
- writeFileSync(fileState.path, updatedContent, "utf8");
46
+ await writeFile(fileState.path, updatedContent, "utf8");
49
47
  }
50
48
  isSupportedFile(fileName) {
51
49
  return fileName.endsWith(".ism");
@@ -1,7 +1,6 @@
1
1
  import { MissingPropertyException } from "./file-manager.js";
2
- import { basename } from "node:path";
3
- import { readFileSync, writeFileSync } from "node:fs";
4
- import { applyEdits, modify, parse as parse$1 } from "jsonc-parser";
2
+ import { readFile, writeFile } from "node:fs/promises";
3
+ import { applyEdits, modify, parse } from "jsonc-parser";
5
4
  //#region src/files/json-package.ts
6
5
  /**
7
6
  * A json package file should have a version property, like what can be seen
@@ -33,28 +32,27 @@ var JSONPackage = class {
33
32
  #setStringInJsonc(jsonc, jsonPath, newString) {
34
33
  return applyEdits(jsonc, modify(jsonc, jsonPath, newString, {}));
35
34
  }
36
- read(filePath) {
37
- const fileContents = readFileSync(filePath, "utf8");
35
+ async read(filePath) {
36
+ const fileContents = await readFile(filePath, "utf8");
38
37
  const parseErrors = [];
39
- const parsedJson = parse$1(fileContents, parseErrors, this.#jsoncOptions);
38
+ const parsedJson = parse(fileContents, parseErrors, this.#jsoncOptions);
40
39
  if (parsedJson?.version && parseErrors.length === 0) return {
41
- name: basename(filePath),
42
40
  path: filePath,
43
41
  version: parsedJson.version,
44
42
  isPrivate: typeof parsedJson?.private === "boolean" ? parsedJson.private : true
45
43
  };
46
44
  throw new MissingPropertyException("JSON", "version");
47
45
  }
48
- write(fileState, newVersion) {
49
- let fileContents = readFileSync(fileState.path, "utf8");
50
- const parsedJson = parse$1(fileContents, [], this.#jsoncOptions);
46
+ async write(fileState, newVersion) {
47
+ let fileContents = await readFile(fileState.path, "utf8");
48
+ const parsedJson = parse(fileContents, [], this.#jsoncOptions);
51
49
  fileContents = this.#setStringInJsonc(fileContents, ["version"], newVersion);
52
50
  if (parsedJson?.packages?.[""]) fileContents = this.#setStringInJsonc(fileContents, [
53
51
  "packages",
54
52
  "",
55
53
  "version"
56
54
  ], newVersion);
57
- writeFileSync(fileState.path, fileContents, "utf8");
55
+ await writeFile(fileState.path, fileContents, "utf8");
58
56
  }
59
57
  isSupportedFile(fileName) {
60
58
  return fileName.endsWith(".json") || fileName.endsWith(".jsonc");
@@ -1,6 +1,5 @@
1
1
  import { MissingPropertyException } from "./file-manager.js";
2
- import { basename } from "node:path";
3
- import { readFileSync, writeFileSync } from "node:fs";
2
+ import { readFile, writeFile } from "node:fs/promises";
4
3
  import * as cheerio from "cheerio/slim";
5
4
  //#region src/files/ms-build-project.ts
6
5
  /**
@@ -22,22 +21,21 @@ var MSBuildProject = class {
22
21
  xmlMode: true,
23
22
  xml: { decodeEntities: false }
24
23
  };
25
- read(filePath) {
26
- const fileContents = readFileSync(filePath, "utf8");
24
+ async read(filePath) {
25
+ const fileContents = await readFile(filePath, "utf8");
27
26
  const version = cheerio.load(fileContents, this.#cheerioOptions)("Project > PropertyGroup > Version").text();
28
27
  if (version) return {
29
- name: basename(filePath),
30
28
  path: filePath,
31
29
  version
32
30
  };
33
31
  throw new MissingPropertyException("MSBuild", "Version");
34
32
  }
35
- write(fileState, newVersion) {
36
- const fileContents = readFileSync(fileState.path, "utf8");
33
+ async write(fileState, newVersion) {
34
+ const fileContents = await readFile(fileState.path, "utf8");
37
35
  const $ = cheerio.load(fileContents, this.#cheerioOptions);
38
36
  $("Project > PropertyGroup > Version").text(newVersion);
39
37
  const updatedContent = $.xml().replaceAll("\"/>", "\" />");
40
- writeFileSync(fileState.path, updatedContent, "utf8");
38
+ await writeFile(fileState.path, updatedContent, "utf8");
41
39
  }
42
40
  isSupportedFile(fileName) {
43
41
  return [
@@ -1,6 +1,5 @@
1
1
  import { MissingPropertyException } from "./file-manager.js";
2
- import { basename } from "node:path";
3
- import { readFileSync, writeFileSync } from "node:fs";
2
+ import { readFile, writeFile } from "node:fs/promises";
4
3
  //#region src/files/plain-text.ts
5
4
  /**
6
5
  * A plain text file will have just the version as the content.
@@ -11,17 +10,16 @@ import { readFileSync, writeFileSync } from "node:fs";
11
10
  * ```
12
11
  */
13
12
  var PlainText = class {
14
- read(filePath) {
15
- const fileContents = readFileSync(filePath, "utf8").trim();
13
+ async read(filePath) {
14
+ const fileContents = (await readFile(filePath, "utf8")).trim();
16
15
  if (fileContents) return {
17
- name: basename(filePath),
18
16
  path: filePath,
19
17
  version: fileContents
20
18
  };
21
19
  throw new MissingPropertyException("Plain Text", "version");
22
20
  }
23
- write(fileState, newVersion) {
24
- writeFileSync(fileState.path, newVersion, "utf8");
21
+ async write(fileState, newVersion) {
22
+ await writeFile(fileState.path, newVersion, "utf8");
25
23
  }
26
24
  isSupportedFile(fileName) {
27
25
  return fileName.endsWith("version.txt");
@@ -1,7 +1,6 @@
1
1
  import { MissingPropertyException } from "./file-manager.js";
2
- import { basename } from "node:path";
3
- import { readFileSync, writeFileSync } from "node:fs";
4
- import { parse as parse$1, parseDocument } from "yaml";
2
+ import { readFile, writeFile } from "node:fs/promises";
3
+ import { parse, parseDocument } from "yaml";
5
4
  //#region src/files/yaml-package.ts
6
5
  /**
7
6
  * A yaml package file should have a version property on the top level, like what can be seen
@@ -16,38 +15,18 @@ import { parse as parse$1, parseDocument } from "yaml";
16
15
  * ```
17
16
  */
18
17
  var YAMLPackage = class {
19
- /**
20
- * If the version is returned with a "+" symbol in the value then the version might be from a
21
- * flutter `pubspec.yaml` file, if so we want to retain the input builderNumber by splitting it
22
- * and joining it again later.
23
- */
24
- #handleBuildNumber(fileVersion) {
25
- const [version, builderNumber] = fileVersion.split("+");
26
- if (/^\d+$/.test(builderNumber)) return {
27
- version,
28
- builderNumber
18
+ async read(filePath) {
19
+ const fileVersion = parse(await readFile(filePath, "utf8"))?.version;
20
+ if (fileVersion) return {
21
+ path: filePath,
22
+ version: fileVersion
29
23
  };
30
- return { version: fileVersion };
31
- }
32
- read(filePath) {
33
- const fileVersion = parse$1(readFileSync(filePath, "utf-8"))?.version;
34
- if (fileVersion) {
35
- const parsedVersion = this.#handleBuildNumber(fileVersion);
36
- return {
37
- name: basename(filePath),
38
- path: filePath,
39
- version: parsedVersion.version || "",
40
- builderNumber: parsedVersion.builderNumber ?? void 0
41
- };
42
- }
43
24
  throw new MissingPropertyException("YAML", "version");
44
25
  }
45
- write(fileState, newVersion) {
46
- const yamlDocument = parseDocument(readFileSync(fileState.path, "utf8"));
47
- let newFileVersion = newVersion;
48
- if (fileState.builderNumber !== void 0) newFileVersion += `+${fileState.builderNumber}`;
49
- yamlDocument.set("version", newFileVersion);
50
- writeFileSync(fileState.path, yamlDocument.toString(), "utf8");
26
+ async write(fileState, newVersion) {
27
+ const yamlDocument = parseDocument(await readFile(fileState.path, "utf8"));
28
+ yamlDocument.set("version", newVersion);
29
+ await writeFile(fileState.path, yamlDocument.toString(), "utf8");
51
30
  }
52
31
  isSupportedFile(fileName) {
53
32
  return fileName.endsWith(".yaml") || fileName.endsWith(".yml");
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ParserOptions, createParserOptions } from "./commit-parser/options.js";
2
- import { ChangelogPresetConfig, ChangelogPresetConfigType, Config, ForkConfig } from "./config/types.js";
3
2
  import { Logger } from "./services/logger.js";
4
3
  import { FileManager, FileState, IFileManager, MissingPropertyException } from "./files/file-manager.js";
4
+ import { ChangelogPresetConfig, ChangelogPresetConfigType, Config, ForkConfig } from "./config/types.js";
5
5
  import { Git } from "./services/git.js";
6
6
  import { inspect } from "./commands/inspect.js";
7
7
  import { main } from "./commands/main.js";
@@ -9,7 +9,7 @@ import { validateConfig } from "./commands/validate-config.js";
9
9
  import { Commit, CommitMerge, CommitNote, CommitReference, CommitRevert } from "./commit-parser/types.js";
10
10
  import { CommitParser } from "./commit-parser/commit-parser.js";
11
11
  import { filterRevertedCommits } from "./commit-parser/filter-reverted-commits.js";
12
- import { ForkConfigSchema } from "./config/schema.js";
12
+ import { ForkConfigJSONSchema, ForkConfigJSSchema } from "./config/schema.js";
13
13
  import { defineConfig } from "./config/define-config.js";
14
14
  import { getUserConfig } from "./config/user-config.js";
15
15
  import { CommitsSinceTag, getCommitsSinceTag } from "./process/get-commits.js";
@@ -18,4 +18,4 @@ import { NextVersion, getNextVersion } from "./process/get-next-version.js";
18
18
  import { updateChangelog } from "./process/changelog.js";
19
19
  import { commitChanges } from "./process/commit.js";
20
20
  import { tagChanges } from "./process/tag.js";
21
- export { type ChangelogPresetConfig, type ChangelogPresetConfigType, type Commit, type CommitMerge, type CommitNote, CommitParser, type CommitReference, type CommitRevert, type CommitsSinceTag, type Config, type CurrentVersion, FileManager, type FileState, type ForkConfig, ForkConfigSchema, Git, type IFileManager, Logger, MissingPropertyException, type NextVersion, type ParserOptions, commitChanges, createParserOptions, defineConfig, filterRevertedCommits, getCommitsSinceTag, getCurrentVersion, getNextVersion, getUserConfig, inspect, main, tagChanges, updateChangelog, validateConfig };
21
+ export { type ChangelogPresetConfig, type ChangelogPresetConfigType, type Commit, type CommitMerge, type CommitNote, CommitParser, type CommitReference, type CommitRevert, type CommitsSinceTag, type Config, type CurrentVersion, FileManager, type FileState, type ForkConfig, ForkConfigJSONSchema, ForkConfigJSSchema, Git, type IFileManager, Logger, MissingPropertyException, type NextVersion, type ParserOptions, commitChanges, createParserOptions, defineConfig, filterRevertedCommits, getCommitsSinceTag, getCurrentVersion, getNextVersion, getUserConfig, inspect, main, tagChanges, updateChangelog, validateConfig };
package/dist/index.js CHANGED
@@ -10,10 +10,10 @@ import { commitChanges } from "./process/commit.js";
10
10
  import { tagChanges } from "./process/tag.js";
11
11
  import { main } from "./commands/main.js";
12
12
  import { validateConfig } from "./commands/validate-config.js";
13
- import { ForkConfigSchema } from "./config/schema.js";
13
+ import { ForkConfigJSONSchema, ForkConfigJSSchema } from "./config/schema.js";
14
14
  import { defineConfig } from "./config/define-config.js";
15
15
  import { Git } from "./services/git.js";
16
16
  import { getUserConfig } from "./config/user-config.js";
17
17
  import { FileManager, MissingPropertyException } from "./files/file-manager.js";
18
18
  import { Logger } from "./services/logger.js";
19
- export { CommitParser, FileManager, ForkConfigSchema, Git, Logger, MissingPropertyException, commitChanges, createParserOptions, defineConfig, filterRevertedCommits, getCommitsSinceTag, getCurrentVersion, getNextVersion, getUserConfig, inspect, main, tagChanges, updateChangelog, validateConfig };
19
+ export { CommitParser, FileManager, ForkConfigJSONSchema, ForkConfigJSSchema, Git, Logger, MissingPropertyException, commitChanges, createParserOptions, defineConfig, filterRevertedCommits, getCommitsSinceTag, getCurrentVersion, getNextVersion, getUserConfig, inspect, main, tagChanges, updateChangelog, validateConfig };
@@ -1,5 +1,5 @@
1
- import { ForkConfig } from "../config/types.js";
2
1
  import { Logger } from "../services/logger.js";
2
+ import { ForkConfig } from "../config/types.js";
3
3
 
4
4
  //#region src/process/changelog.d.ts
5
5
  declare function updateChangelog(config: ForkConfig, logger: Logger, nextVersion: string): Promise<void>;
@@ -1,6 +1,6 @@
1
1
  import { fileExists } from "../utils/file-state.js";
2
2
  import { resolve } from "node:path";
3
- import { readFileSync, writeFileSync } from "node:fs";
3
+ import { readFile, writeFile } from "node:fs/promises";
4
4
  import conventionalChangelog from "conventional-changelog";
5
5
  //#region src/process/changelog.ts
6
6
  /**
@@ -13,9 +13,9 @@ const RELEASE_PATTERN = /(^#+ \[?[0-9]+\.[0-9]+\.[0-9]+|<a name=)/m;
13
13
  * Get the existing changelog content from the latest release onwards.
14
14
  * @see {@link RELEASE_PATTERN}
15
15
  */
16
- function getOldReleaseContent(filePath, exists) {
16
+ async function getOldReleaseContent(filePath, exists) {
17
17
  if (exists) {
18
- const fileContents = readFileSync(filePath, "utf-8");
18
+ const fileContents = await readFile(filePath, "utf8");
19
19
  const oldContentStart = fileContents.search(RELEASE_PATTERN);
20
20
  if (oldContentStart !== -1) return fileContents.substring(oldContentStart);
21
21
  }
@@ -56,11 +56,11 @@ async function updateChangelog(config, logger, nextVersion) {
56
56
  const changelogPath = resolve(config.path, config.changelog);
57
57
  if (!config.dryRun && !fileExists(changelogPath)) {
58
58
  logger.log(`Creating changelog: ${changelogPath}`);
59
- writeFileSync(changelogPath, "\n", "utf8");
59
+ await writeFile(changelogPath, "\n", "utf8");
60
60
  } else logger.log(`Updating changelog: ${changelogPath}`);
61
- const oldContent = getOldReleaseContent(changelogPath, fileExists(changelogPath));
61
+ const oldContent = await getOldReleaseContent(changelogPath, fileExists(changelogPath));
62
62
  const newContent = await getNewReleaseContent(config, logger, nextVersion);
63
- if (!config.dryRun && newContent) writeFileSync(changelogPath, `${config.header}
63
+ if (!config.dryRun && newContent) await writeFile(changelogPath, `${config.header}
64
64
  ${newContent}
65
65
  ${oldContent}
66
66
  `.trim(), "utf8");
@@ -1,6 +1,6 @@
1
- import { ForkConfig } from "../config/types.js";
2
1
  import { Logger } from "../services/logger.js";
3
2
  import { FileState } from "../files/file-manager.js";
3
+ import { ForkConfig } from "../config/types.js";
4
4
  import { Git } from "../services/git.js";
5
5
 
6
6
  //#region src/process/commit.d.ts
@@ -1,5 +1,5 @@
1
- import { ForkConfig } from "../config/types.js";
2
1
  import { Logger } from "../services/logger.js";
2
+ import { ForkConfig } from "../config/types.js";
3
3
  import { Git } from "../services/git.js";
4
4
  import { Commit } from "../commit-parser/types.js";
5
5
 
@@ -1,6 +1,6 @@
1
- import { ForkConfig } from "../config/types.js";
2
1
  import { Logger } from "../services/logger.js";
3
2
  import { FileManager, FileState } from "../files/file-manager.js";
3
+ import { ForkConfig } from "../config/types.js";
4
4
  import { Git } from "../services/git.js";
5
5
 
6
6
  //#region src/process/get-current-version.d.ts
@@ -8,7 +8,7 @@ async function getCurrentVersion(config, logger, git, fileManager, filesToUpdate
8
8
  logger.debug(`[Git Ignored] ${file}`);
9
9
  continue;
10
10
  }
11
- const fileState = fileManager.read(file);
11
+ const fileState = await fileManager.read(file);
12
12
  if (fileState) {
13
13
  files.push(fileState);
14
14
  if (!config.currentVersion) versions.add(fileState.version);
@@ -1,5 +1,5 @@
1
- import { ForkConfig } from "../config/types.js";
2
1
  import { Logger } from "../services/logger.js";
2
+ import { ForkConfig } from "../config/types.js";
3
3
  import { Commit } from "../commit-parser/types.js";
4
4
  import { ReleaseType } from "semver";
5
5
 
@@ -1,5 +1,5 @@
1
- import { ForkConfig } from "../config/types.js";
2
1
  import { Logger } from "../services/logger.js";
2
+ import { ForkConfig } from "../config/types.js";
3
3
  import { Git } from "../services/git.js";
4
4
 
5
5
  //#region src/process/tag.d.ts
@@ -0,0 +1,37 @@
1
+ //#region src/utils/extract-build-metadata.ts
2
+ /**
3
+ * This function is used to separate build metadata from the version string.
4
+ * Build metadata is denoted by a plus sign (`+`) followed by a string of characters.
5
+ *
6
+ * Example version strings including build metadata:
7
+ * - `1.2.3+49a3f2b`
8
+ * - `1.2.3-0+49a3f2b`
9
+ * - `1.2.3-alpha.0+49a3f2b`
10
+ *
11
+ * In the above examples, the build metadata is `+49a3f2b`, which should be
12
+ * ignored when determining the next version.
13
+ *
14
+ * @param version The version string to extract build metadata from.
15
+ * @return Both the version string without build metadata and the extracted build metadata (if any).
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const versionWithMetadata = "1.2.3+49a3f2b";
20
+ * const { version, buildMetadata } = extractBuildMetadata(versionWithMetadata);
21
+ * console.log(version); // Output: "1.2.3"
22
+ * console.log(buildMetadata); // Output: "49a3f2b"
23
+ * ```
24
+ */
25
+ function extractBuildMetadata(version) {
26
+ const plusIndex = version.indexOf("+");
27
+ if (plusIndex === -1) return {
28
+ version,
29
+ buildMetadata: void 0
30
+ };
31
+ return {
32
+ version: version.substring(0, plusIndex),
33
+ buildMetadata: version.substring(plusIndex + 1) || void 0
34
+ };
35
+ }
36
+ //#endregion
37
+ export { extractBuildMetadata };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fork-version",
3
- "version": "5.0.2",
3
+ "version": "5.1.1",
4
4
  "license": "MIT",
5
5
  "description": "Fork-Version automates version control tasks such as determining, updating, and committing versions, files, and changelogs, simplifying the process when adhering to the conventional commit standard.",
6
6
  "keywords": [