From 3eb32ff036dacb334ed90b7a26c82c21e3ef7391 Mon Sep 17 00:00:00 2001
From: wtgtybhertgeghgtwtg <wtgtybhertgeghgtwtg@gmail.com>
Date: Tue, 31 May 2022 19:13:52 -0700
Subject: [PATCH] chore: update required `node` version (#268)

BREAKING CHANGE: drops `node@12`
---
 .circleci/config.yml          | 10 +++++-----
 .eslintrc.js                  |  2 +-
 __mocks__/fs.ts               | 20 --------------------
 __mocks__/fs/promises.ts      | 20 ++++++++++++++++++++
 __tests__/load-config.test.ts |  4 ++--
 package.json                  | 11 +++++------
 rollup.config.js              |  2 +-
 source/load-file-property.ts  | 22 ++++++++++++++--------
 8 files changed, 48 insertions(+), 43 deletions(-)
 create mode 100644 __mocks__/fs/promises.ts

diff --git a/.circleci/config.yml b/.circleci/config.yml
index 9bedab4..29cfdbe 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -1,7 +1,7 @@
 jobs:
   lint:
     docker:
-      - image: circleci/node:12
+      - image: circleci/node:14
     steps:
       - checkout
       - restore_cache:
@@ -18,7 +18,7 @@ jobs:
 
   release:
     docker:
-      - image: circleci/node:12
+      - image: circleci/node:14
     steps:
       - checkout
       - restore_cache:
@@ -31,11 +31,11 @@ jobs:
             - node_modules
           key: v1-dependencies-{{ checksum "package.json" }}
       - run: yarn build
-      - run: npx semantic-release@17
+      - run: npx semantic-release
 
   test:
     docker:
-      - image: circleci/node:12
+      - image: circleci/node:14
     steps:
       - checkout
       - restore_cache:
@@ -52,7 +52,7 @@ jobs:
 
   test-distribution:
     docker:
-      - image: circleci/node:12
+      - image: circleci/node:14
     steps:
       - checkout
       - restore_cache:
diff --git a/.eslintrc.js b/.eslintrc.js
index 9726dd5..a566512 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -45,7 +45,7 @@ module.exports = {
       },
     },
     {
-      files: ['__mocks__/*.ts', 'scripts/jest/*.js', '.eslintrc.js'],
+      files: ['__mocks__/**/*.ts', 'scripts/jest/*.js', '.eslintrc.js'],
       rules: {
         // Mocks and some configuration files cannot be modules.
         'unicorn/prefer-module': 'off',
diff --git a/__mocks__/fs.ts b/__mocks__/fs.ts
index ff98bf4..6721558 100644
--- a/__mocks__/fs.ts
+++ b/__mocks__/fs.ts
@@ -6,25 +6,6 @@ function _setFiles(newFiles: {[path: string]: Buffer}): void {
   files = mapToMap(newFiles);
 }
 
-const readFile = jest.fn(
-  (
-    path: string,
-    encoding: BufferEncoding,
-    // eslint-disable-next-line @rushstack/no-new-null
-    callback: (error: Error | null, value?: string) => void,
-  ) => {
-    const file = files.get(path);
-    if (file && Buffer.isBuffer(file)) {
-      const decodedFile = file.toString(encoding);
-      // eslint-disable-next-line unicorn/no-null
-      callback(null, decodedFile);
-    } else {
-      const error = new Error('File not found.');
-      callback(error);
-    }
-  },
-);
-
 const readFileSync = jest.fn((path: string, encoding: BufferEncoding) => {
   const file = files.get(path);
   if (file && Buffer.isBuffer(file)) {
@@ -35,6 +16,5 @@ const readFileSync = jest.fn((path: string, encoding: BufferEncoding) => {
 
 module.exports = {
   _setFiles,
-  readFile,
   readFileSync,
 };
diff --git a/__mocks__/fs/promises.ts b/__mocks__/fs/promises.ts
new file mode 100644
index 0000000..d407fbf
--- /dev/null
+++ b/__mocks__/fs/promises.ts
@@ -0,0 +1,20 @@
+import mapToMap from 'map-to-map';
+
+let files: Map<string, Buffer> = new Map();
+
+function _setFiles(newFiles: {[path: string]: Buffer}): void {
+  files = mapToMap(newFiles);
+}
+
+const readFile = jest.fn(async (path: string, encoding: BufferEncoding) => {
+  const file = files.get(path);
+  if (file && Buffer.isBuffer(file)) {
+    return file.toString(encoding);
+  }
+  throw new Error('File not found.');
+});
+
+module.exports = {
+  _setFiles,
+  readFile,
+};
diff --git a/__tests__/load-config.test.ts b/__tests__/load-config.test.ts
index 7e3f68c..b646f42 100644
--- a/__tests__/load-config.test.ts
+++ b/__tests__/load-config.test.ts
@@ -1,6 +1,6 @@
-jest.mock('fs');
+jest.mock('fs/promises');
 
-import fs from 'fs';
+import fs from 'fs/promises';
 import {ConfigError, loadConfig} from '../source';
 
 describe('loadConfig', () => {
diff --git a/package.json b/package.json
index fcff9f9..6fac9c1 100644
--- a/package.json
+++ b/package.json
@@ -10,13 +10,12 @@
     "@babel/core": "^7.15.0",
     "@babel/preset-env": "^7.15.0",
     "@babel/preset-typescript": "^7.15.0",
-    "@commitlint/cli": "^16.0.0",
-    "@commitlint/config-conventional": "^16.0.0",
+    "@commitlint/cli": "^17.0.1",
+    "@commitlint/config-conventional": "^17.0.0",
     "@rushstack/eslint-config": "^2.5.0",
     "@types/jest": "^27.4.1",
     "@types/node": "^16.6.1",
     "babel-jest": "^28.0.2",
-    "builtin-modules": "^3.2.0",
     "eslint": "^8.4.1",
     "eslint-plugin-eslint-comments": "^3.2.0",
     "eslint-plugin-unicorn": "^42.0.0",
@@ -25,11 +24,11 @@
     "prettier": "2.6.2",
     "rimraf": "^3.0.2",
     "rollup": "^2.56.0",
-    "rollup-plugin-ts": "^2.0.0",
-    "typescript": "~4.6.0"
+    "rollup-plugin-ts": "^3.0.1",
+    "typescript": "~4.7.2"
   },
   "engines": {
-    "node": ">=12"
+    "node": ">=14"
   },
   "files": [
     "distribution",
diff --git a/rollup.config.js b/rollup.config.js
index 5861830..7740d3b 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -1,4 +1,4 @@
-import builtinModules from 'builtin-modules';
+import {builtinModules} from 'module';
 import ts from 'rollup-plugin-ts';
 import {fileURLToPath} from 'url';
 import packageJson from './package.json';
diff --git a/source/load-file-property.ts b/source/load-file-property.ts
index 8d45d4f..510a7ea 100644
--- a/source/load-file-property.ts
+++ b/source/load-file-property.ts
@@ -1,14 +1,20 @@
-import {readFile} from 'fs';
+import {readFile} from 'fs/promises';
 import formatProperty from './format-property';
 import {FileConfig, PropertyResult} from './types';
 
-export default function loadFileProperty<Value>(
+async function tryToReadFile(
+  propertyConfig: FileConfig<unknown>,
+): Promise<PropertyResult<string>> {
+  const {encoding = 'utf8', filePath} = propertyConfig;
+  try {
+    return {error: false, value: await readFile(filePath, encoding)};
+  } catch (error) {
+    return {error: error as Error, value: undefined};
+  }
+}
+
+export default async function loadFileProperty<Value>(
   propertyConfig: FileConfig<Value>,
 ): Promise<PropertyResult<string | Value>> {
-  const {encoding = 'utf8', filePath} = propertyConfig;
-  return new Promise((resolve) => {
-    readFile(filePath, encoding, (error, value) => {
-      resolve(formatProperty({error: error ?? false, value}, propertyConfig));
-    });
-  });
+  return formatProperty(await tryToReadFile(propertyConfig), propertyConfig);
 }