Repository: simple-icons/simple-icons
Pull request #9237 (2023-08-08 01:50:30)

Refactor tests and scripts
- Don't allow single `'` nor doble quotes `"` in URLs.
- Explicitly import `process` module with `node:` protocol.
- Refactor, normalize usage of APIs and remove duplicated code.
- Updated *SmugMug* source as had a single quote and the current URL is returning 404 code.


Patch:
diff --git a/.svglintrc.mjs b/.svglintrc.mjs
index e20d019b..861f200f 100644
--- a/.svglintrc.mjs
+++ b/.svglintrc.mjs
@@ -1,6 +1,8 @@
-import fs from 'node:fs';
+import fs from 'node:fs/promises';
 import path from 'node:path';
+import process from 'node:process';
 import {
+  SVG_PATH_REGEX,
   getDirnameFromImportMeta,
   htmlFriendlyToTitle,
   collator,
@@ -19,16 +21,17 @@ const htmlNamedEntitiesFile = path.join(
 );
 const svglintIgnoredFile = path.join(__dirname, '.svglint-ignored.json');
 
-const data = JSON.parse(fs.readFileSync(dataFile, 'utf8'));
+const data = JSON.parse(await fs.readFile(dataFile, 'utf8'));
 const htmlNamedEntities = JSON.parse(
-  fs.readFileSync(htmlNamedEntitiesFile, 'utf8'),
+  await fs.readFile(htmlNamedEntitiesFile, 'utf8'),
+);
+const svglintIgnores = JSON.parse(
+  await fs.readFile(svglintIgnoredFile, 'utf8'),
 );
-const svglintIgnores = JSON.parse(fs.readFileSync(svglintIgnoredFile, 'utf8'));
 
 const svgRegexp =
   /^<svg( [^\s]*=".*"){3}><title>.*<\/title><path d=".*"\/><\/svg>$/;
 const negativeZerosRegexp = /-0(?=[^\.]|[\s\d\w]|$)/g;
-const svgPathRegexp = /^[Mm][MmZzLlHhVvCcSsQqTtAaEe0-9\-,. ]+$/;
 
 const iconSize = 24;
 const iconTargetCenter = iconSize / 2;
@@ -140,14 +143,14 @@ const getIconPathSegments = memoize((iconPath) => parsePath(iconPath));
 const getIconPathBbox = memoize((iconPath) => svgPathBbox(iconPath));
 
 if (updateIgnoreFile) {
-  process.on('exit', () => {
+  process.on('exit', async () => {
     // ensure object output order is consistent due to async svglint processing
     const sorted = sortObjectByKey(iconIgnored);
     for (const linterName in sorted) {
       sorted[linterName] = sortObjectByValue(sorted[linterName]);
     }
 
-    fs.writeFileSync(ignoreFile, JSON.stringify(sorted, null, 2) + '\n', {
+    await fs.writeFile(ignoreFile, JSON.stringify(sorted, null, 2) + '\n', {
       flag: 'w',
     });
   });
@@ -197,7 +200,7 @@ export default {
       {
         // ensure that the path element only has the 'd' attribute
         // (no style, opacity, etc.)
-        d: svgPathRegexp,
+        d: SVG_PATH_REGEX,
         'rule::selector': 'svg > path',
         'rule::whitelist': true,
       },
@@ -908,7 +911,7 @@ export default {
 
         const iconPath = getIconPath($, filepath);
 
-        if (!svgPathRegexp.test(iconPath)) {
+        if (!SVG_PATH_REGEX.test(iconPath)) {
           let errorMsg = 'Invalid path format',
             reason;
 
@@ -920,7 +923,7 @@ export default {
             reporter.error(`${errorMsg}: ${reason}`);
           }
 
-          const validPathCharacters = svgPathRegexp.source.replace(
+          const validPathCharacters = SVG_PATH_REGEX.source.replace(
               /[\[\]+^$]/g,
               '',
             ),
diff --git a/_data/simple-icons.json b/_data/simple-icons.json
index b39a7fe8..bfb76a23 100644
--- a/_data/simple-icons.json
+++ b/_data/simple-icons.json
@@ -11461,7 +11461,7 @@
         {
             "title": "SmugMug",
             "hex": "6DB944",
-            "source": "https://help.smugmug.com/using-smugmug's-logo-HJulJePkEBf"
+            "source": "https://www.smugmughelp.com/articles/409-smugmug-s-logo-and-usage"
         },
         {
             "title": "Snapchat",
diff --git a/scripts/add-icon-data.js b/scripts/add-icon-data.js
index c816a26e..cc9b903a 100644
--- a/scripts/add-icon-data.js
+++ b/scripts/add-icon-data.js
@@ -1,3 +1,4 @@
+import process from 'node:process';
 import chalk from 'chalk';
 import { input, confirm, checkbox } from '@inquirer/prompts';
 import getRelativeLuminance from 'get-relative-luminance';
@@ -27,10 +28,10 @@ const titleValidator = (text) => {
 };
 
 const hexValidator = (text) =>
-  hexPattern.test(text) ? true : 'This should be a valid hex code';
+  hexPattern.test(text) || 'This should be a valid hex code';
 
 const sourceValidator = (text) =>
-  URL_REGEX.test(text) ? true : 'This should be a secure URL';
+  URL_REGEX.test(text) || 'This should be a secure URL';
 
 const hexTransformer = (text) => {
   const color = normalizeColor(text);
diff --git a/scripts/build/package.js b/scripts/build/package.js
index 4a88e8a9..05ca17bb 100644
--- a/scripts/build/package.js
+++ b/scripts/build/package.js
@@ -40,9 +40,6 @@ const build = async () => {
   const escape = (value) => {
     return value.replace(/(?<!\\)'/g, "\\'");
   };
-  const iconToKeyValue = (icon) => {
-    return `'${icon.slug}':${iconToObject(icon)}`;
-  };
   const licenseToObject = (license) => {
     if (license === undefined) {
       return;
@@ -82,7 +79,7 @@ const build = async () => {
     icons.map(async (icon) => {
       const filename = getIconSlug(icon);
       const svgFilepath = path.resolve(iconsDir, `${filename}.svg`);
-      icon.svg = (await fs.readFile(svgFilepath, UTF8)).replace(/\r?\n/, '');
+      icon.svg = await fs.readFile(svgFilepath, UTF8);
       icon.path = svgToPath(icon.svg);
       icon.slug = filename;
       const iconObject = iconToObject(icon);
@@ -96,11 +93,11 @@ const build = async () => {
   const iconsBarrelMjs = [];
 
   buildIcons.sort((a, b) => collator.compare(a.icon.title, b.icon.title));
-  buildIcons.forEach(({ iconObject, iconExportName }) => {
+  for (const { iconObject, iconExportName } of buildIcons) {
     iconsBarrelDts.push(`export const ${iconExportName}:I;`);
     iconsBarrelJs.push(`${iconExportName}:${iconObject},`);
     iconsBarrelMjs.push(`export const ${iconExportName}=${iconObject}`);
-  });
+  }
 
   // constants used in templates to reduce package size
   const constantsString = `const a='<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>',b='</title><path d="',c='"/></svg>';`;
diff --git a/scripts/get-filename.js b/scripts/get-filename.js
index 0cf5369d..826b43c3 100644
--- a/scripts/get-filename.js
+++ b/scripts/get-filename.js
@@ -4,6 +4,7 @@
  * icon SVG filename to standard output.
  */
 
+import process from 'node:process';
 import { titleToSlug } from '../sdk.mjs';
 
 if (process.argv.length < 3) {
diff --git a/scripts/lint/jsonlint.js b/scripts/lint/jsonlint.js
index b7f93fb2..0479dc98 100644
--- a/scripts/lint/jsonlint.js
+++ b/scripts/lint/jsonlint.js
@@ -3,22 +3,18 @@
  * CLI tool to run jsonschema on the simple-icons.json data file.
  */
 
-import path from 'node:path';
+import process from 'node:process';
 import { Validator } from 'jsonschema';
-import { getDirnameFromImportMeta, getIconsData } from '../../sdk.mjs';
+import { getIconsData } from '../../sdk.mjs';
 import { getJsonSchemaData } from '../utils.js';
 
 const icons = await getIconsData();
-const __dirname = getDirnameFromImportMeta(import.meta.url);
-const schema = await getJsonSchemaData(path.resolve(__dirname, '..', '..'));
+const schema = await getJsonSchemaData();
 
 const validator = new Validator();
 const result = validator.validate({ icons }, schema);
 if (result.errors.length > 0) {
-  result.errors.forEach((error) => {
-    console.error(error);
-  });
-
+  result.errors.forEach((error) => console.error(error));
   console.error(`Found ${result.errors.length} error(s) in simple-icons.json`);
   process.exit(1);
 }
diff --git a/scripts/lint/ourlint.js b/scripts/lint/ourlint.js
index 85dabd6c..49abcb20 100644
--- a/scripts/lint/ourlint.js
+++ b/scripts/lint/ourlint.js
@@ -4,6 +4,7 @@
  * linters (e.g. jsonlint/svglint).
  */
 
+import process from 'node:process';
 import { URL } from 'node:url';
 import fakeDiff from 'fake-diff';
 import { getIconsDataString, normalizeNewlines, collator } from '../../sdk.mjs';
@@ -46,7 +47,7 @@ const TESTS = {
   },
 
   /* Check the formatting of the data file */
-  prettified: async (data, dataString) => {
+  prettified: (data, dataString) => {
     const normalizedDataString = normalizeNewlines(dataString);
     const dataPretty = `${JSON.stringify(data, null, 4)}\n`;
 
@@ -66,8 +67,7 @@ const TESTS = {
     const allUrlFields = [
       ...new Set(
         data.icons
-          .map((icon) => [icon.source, icon.guidelines, icon.license?.url])
-          .flat()
+          .flatMap((icon) => [icon.source, icon.guidelines, icon.license?.url])
           .filter(Boolean),
       ),
     ];
@@ -84,19 +84,14 @@ const TESTS = {
   },
 };
 
-// execute all tests and log all errors
-(async () => {
-  const dataString = await getIconsDataString();
-  const data = JSON.parse(dataString);
+const dataString = await getIconsDataString();
+const data = JSON.parse(dataString);
 
-  const errors = (
-    await Promise.all(
-      Object.keys(TESTS).map((test) => TESTS[test](data, dataString)),
-    )
-  ).filter(Boolean);
+const errors = (
+  await Promise.all(Object.values(TESTS).map((test) => test(data, dataString)))
+).filter(Boolean);
 
-  if (errors.length > 0) {
-    errors.forEach((error) => console.error(`\u001b[31m${error}\u001b[0m`));
-    process.exit(1);
-  }
-})();
+if (errors.length > 0) {
+  errors.forEach((error) => console.error(`\u001b[31m${error}\u001b[0m`));
+  process.exit(1);
+}
diff --git a/scripts/release/update-cdn-urls.js b/scripts/release/update-cdn-urls.js
index 6b0a52c8..f6be9e9b 100644
--- a/scripts/release/update-cdn-urls.js
+++ b/scripts/release/update-cdn-urls.js
@@ -4,7 +4,8 @@
  * NPM package manifest. Does nothing if the README.md is already up-to-date.
  */
 
-import fs from 'node:fs';
+import process from 'node:process';
+import fs from 'node:fs/promises';
 import path from 'node:path';
 import { getDirnameFromImportMeta } from '../../sdk.mjs';
 
@@ -19,31 +20,31 @@ const getMajorVersion = (semVerVersion) => {
   return parseInt(majorVersionAsString);
 };
 
-const getManifest = () => {
-  const manifestRaw = fs.readFileSync(packageJsonFile, 'utf-8');
+const getManifest = async () => {
+  const manifestRaw = await fs.readFile(packageJsonFile, 'utf-8');
   return JSON.parse(manifestRaw);
 };
 
-const updateVersionInReadmeIfNecessary = (majorVersion) => {
-  let content = fs.readFileSync(readmeFile).toString();
+const updateVersionInReadmeIfNecessary = async (majorVersion) => {
+  let content = await fs.readFile(readmeFile, 'utf8');
 
   content = content.replace(
     /simple-icons@v[0-9]+/g,
     `simple-icons@v${majorVersion}`,
   );
 
-  fs.writeFileSync(readmeFile, content);
+  await fs.writeFile(readmeFile, content);
 };
 
-const main = () => {
+const main = async () => {
   try {
-    const manifest = getManifest();
+    const manifest = await getManifest();
     const majorVersion = getMajorVersion(manifest.version);
-    updateVersionInReadmeIfNecessary(majorVersion);
+    await updateVersionInReadmeIfNecessary(majorVersion);
   } catch (error) {
     console.error('Failed to update CDN version number:', error);
     process.exit(1);
   }
 };
 
-main();
+await main();
diff --git a/scripts/release/update-sdk-ts-defs.js b/scripts/release/update-sdk-ts-defs.js
index 6e6a3eec..c87e8762 100644
--- a/scripts/release/update-sdk-ts-defs.js
+++ b/scripts/release/update-sdk-ts-defs.js
@@ -4,7 +4,7 @@
  * to match the current definitions of functions of sdk.mjs.
  */
 
-import fsSync from 'node:fs';
+import process from 'node:process';
 import fs from 'node:fs/promises';
 import path from 'node:path';
 import { execSync } from 'node:child_process';
@@ -45,7 +45,11 @@ const generateSdkMts = async () => {
 };
 
 const generateSdkTs = async () => {
-  fsSync.existsSync(sdkMts) && (await fs.unlink(sdkMts));
+  const fileExists = await fs
+    .access(sdkMts)
+    .then(() => true)
+    .catch(() => false);
+  fileExists && (await fs.unlink(sdkMts));
   await generateSdkMts();
 
   const autogeneratedMsg = '/* The next code is autogenerated from sdk.mjs */';
diff --git a/scripts/release/update-slugs-table.js b/scripts/release/update-slugs-table.js
index 7f99a9c1..fffe85f7 100644
--- a/scripts/release/update-slugs-table.js
+++ b/scripts/release/update-slugs-table.js
@@ -25,14 +25,10 @@ update the script at '${path.relative(rootDir, __filename)}'.
 | :--- | :--- |
 `;
 
-(async () => {
-  const icons = await getIconsData();
-
-  icons.forEach((icon) => {
-    const brandName = icon.title;
-    const brandSlug = getIconSlug(icon);
-    content += `| \`${brandName}\` | \`${brandSlug}\` |\n`;
-  });
-
-  await fs.writeFile(slugsFile, content);
-})();
+const icons = await getIconsData();
+for (const icon of icons) {
+  const brandName = icon.title;
+  const brandSlug = getIconSlug(icon);
+  content += `| \`${brandName}\` | \`${brandSlug}\` |\n`;
+}
+await fs.writeFile(slugsFile, content);
diff --git a/scripts/release/update-svgs-count.js b/scripts/release/update-svgs-count.js
index dfb2ea46..05af0fc1 100644
--- a/scripts/release/update-svgs-count.js
+++ b/scripts/release/update-svgs-count.js
@@ -4,7 +4,9 @@
  * at README every time the number of current icons is more than `updateRange`
  * more than the previous milestone.
  */
-import { promises as fs } from 'node:fs';
+
+import process from 'node:process';
+import fs from 'node:fs/promises';
 import path from 'node:path';
 import { getDirnameFromImportMeta, getIconsData } from '../../sdk.mjs';
 
@@ -12,31 +14,26 @@ const regexMatcher = /Over\s(\d+)\s/;
 const updateRange = 100;
 
 const __dirname = getDirnameFromImportMeta(import.meta.url);
-
 const rootDir = path.resolve(__dirname, '..', '..');
 const readmeFile = path.resolve(rootDir, 'README.md');
 
-(async () => {
-  const readmeContent = await fs.readFile(readmeFile, 'utf-8');
-
-  let overNIconsInReadme;
-  try {
-    overNIconsInReadme = parseInt(regexMatcher.exec(readmeContent)[1]);
-  } catch (err) {
-    console.error(
-      'Failed to obtain number of SVG icons of current milestone in README:',
-      err,
-    );
-    process.exit(1);
-  }
+const readmeContent = await fs.readFile(readmeFile, 'utf-8');
 
-  const nIcons = (await getIconsData()).length;
-  const newNIcons = overNIconsInReadme + updateRange;
+let overNIconsInReadme;
+try {
+  overNIconsInReadme = parseInt(regexMatcher.exec(readmeContent)[1]);
+} catch (err) {
+  console.error(
+    'Failed to obtain number of SVG icons of current milestone in README:',
+    err,
+  );
+  process.exit(1);
+}
 
-  if (nIcons <= newNIcons) {
-    process.exit(0);
-  }
+const nIcons = (await getIconsData()).length;
+const newNIcons = overNIconsInReadme + updateRange;
 
+if (nIcons > newNIcons) {
   const newContent = readmeContent.replace(regexMatcher, `Over ${newNIcons} `);
   await fs.writeFile(readmeFile, newContent);
-})();
+}
diff --git a/scripts/utils.js b/scripts/utils.js
index c0b2c913..dfea7d75 100644
--- a/scripts/utils.js
+++ b/scripts/utils.js
@@ -6,7 +6,7 @@ const __dirname = getDirnameFromImportMeta(import.meta.url);
 
 /**
  * Get JSON schema data.
- * @param {String|undefined} rootDir Path to the root directory of the project.
+ * @param {String} rootDir Path to the root directory of the project.
  */
 export const getJsonSchemaData = async (
   rootDir = path.resolve(__dirname, '..'),
@@ -19,13 +19,13 @@ export const getJsonSchemaData = async (
 /**
  * Write icons data to _data/simple-icons.json.
  * @param {Object} iconsData Icons data object.
- * @param {String|undefined} rootDir Path to the root directory of the project.
+ * @param {String} rootDir Path to the root directory of the project.
  */
 export const writeIconsData = async (
   iconsData,
   rootDir = path.resolve(__dirname, '..'),
 ) => {
-  return fs.writeFile(
+  await fs.writeFile(
     getIconDataPath(rootDir),
     `${JSON.stringify(iconsData, null, 4)}\n`,
     'utf8',
diff --git a/sdk.d.ts b/sdk.d.ts
index e4c08591..9d920813 100644
--- a/sdk.d.ts
+++ b/sdk.d.ts
@@ -62,6 +62,7 @@ export type IconData = {
 /* The next code is autogenerated from sdk.mjs */
 
 export const URL_REGEX: RegExp;
+export const SVG_PATH_REGEX: RegExp;
 export function getDirnameFromImportMeta(importMetaUrl: string): string;
 export function getIconSlug(icon: IconData): string;
 export function svgToPath(svg: string): string;
diff --git a/sdk.mjs b/sdk.mjs
index 8415b9af..1b07f9f2 100644
--- a/sdk.mjs
+++ b/sdk.mjs
@@ -36,7 +36,12 @@ const TITLE_TO_SLUG_RANGE_REGEX = /[^a-z0-9]/g;
 /**
  * Regex to validate HTTPs URLs.
  */
-export const URL_REGEX = /^https:\/\/[^\s]+$/;
+export const URL_REGEX = /^https:\/\/[^\s"']+$/;
+
+/**
+ * Regex to validate SVG paths.
+ */
+export const SVG_PATH_REGEX = /^m[-mzlhvcsqtae0-9,. ]+$/i;
 
 /**
  * Get the directory name where this file is located from `import.meta.url`,
@@ -59,7 +64,7 @@ export const getIconSlug = (icon) => icon.slug || titleToSlug(icon.title);
  * @param {String} svg The icon SVG content
  * @returns {String} The path from the icon SVG content
  **/
-export const svgToPath = (svg) => svg.match(/<path\s+d="([^"]*)/)[1];
+export const svgToPath = (svg) => svg.split('"', 8)[7];
 
 /**
  * Converts a brand title into a slug/filename.
@@ -83,8 +88,7 @@ export const titleToSlug = (title) =>
  */
 export const slugToVariableName = (slug) => {
   const slugFirstLetter = slug[0].toUpperCase();
-  const slugRest = slug.slice(1);
-  return `si${slugFirstLetter}${slugRest}`;
+  return `si${slugFirstLetter}${slug.slice(1)}`;
 };
 
 /**
@@ -189,13 +193,11 @@ export const getThirdPartyExtensions = async (
 ) =>
   normalizeNewlines(await fs.readFile(readmePath, 'utf8'))
     .split('## Third-Party Extensions\n\n')[1]
-    .split('\n\n')[0]
+    .split('\n\n', 1)[0]
     .split('\n')
     .slice(2)
     .map((line) => {
       let [module, author] = line.split(' | ');
-
-      // README shipped with package has not Github theme image links
       module = module.split('<img src="')[0];
       return {
         module: {


Test patch:
diff --git a/tests/docs.test.js b/tests/docs.test.js
index 5bcfeb87..c88b460e 100644
--- a/tests/docs.test.js
+++ b/tests/docs.test.js
@@ -1,15 +1,22 @@
-import fs from 'node:fs';
+import fs from 'node:fs/promises';
 import path from 'node:path';
-import { describe, test } from 'mocha';
+import { test } from 'mocha';
 import { strict as assert } from 'node:assert';
-import { getThirdPartyExtensions, getDirnameFromImportMeta } from '../sdk.mjs';
+import {
+  getThirdPartyExtensions,
+  getDirnameFromImportMeta,
+  URL_REGEX,
+} from '../sdk.mjs';
 
 const __dirname = getDirnameFromImportMeta(import.meta.url);
 const root = path.dirname(__dirname);
+const getLinksRegex = new RegExp(
+  URL_REGEX.source.replace('^https', 'https?'),
+  'gm',
+);
 
 test('README third party extensions must be alphabetically sorted', async () => {
-  const readmePath = path.join(root, 'README.md');
-  const thirdPartyExtensions = await getThirdPartyExtensions(readmePath);
+  const thirdPartyExtensions = await getThirdPartyExtensions();
   assert.ok(thirdPartyExtensions.length > 0);
 
   const thirdPartyExtensionsNames = thirdPartyExtensions.map(
@@ -27,22 +34,21 @@ test('README third party extensions must be alphabetically sorted', async () =>
 test('Only allow HTTPS links in documentation pages', async () => {
   const ignoreHttpLinks = ['http://www.w3.org/2000/svg'];
 
-  const docsFiles = fs
-    .readdirSync(root)
-    .filter((fname) => fname.endsWith('.md'));
+  const docsFiles = (await fs.readdir(root)).filter((fname) =>
+    fname.endsWith('.md'),
+  );
 
-  const linksGetter = new RegExp('http://[^\\s"\']+', 'g');
-  for (let docsFile of docsFiles) {
+  for (const docsFile of docsFiles) {
     const docsFilePath = path.join(root, docsFile);
-    const docsFileContent = fs.readFileSync(docsFilePath, 'utf8');
+    const docsFileContent = await fs.readFile(docsFilePath, 'utf8');
 
-    Array.from(docsFileContent.matchAll(linksGetter)).forEach((match) => {
+    for (const match of docsFileContent.matchAll(getLinksRegex)) {
       const link = match[0];
       assert.ok(
         ignoreHttpLinks.includes(link) || link.startsWith('https://'),
         `Link '${link}' in '${docsFile}' (at index ${match.index})` +
           ` must use the HTTPS protocol.`,
       );
-    });
+    }
   }
 });
diff --git a/tests/index.test.js b/tests/index.test.js
index a8f7c413..19e735ff 100644
--- a/tests/index.test.js
+++ b/tests/index.test.js
@@ -2,14 +2,10 @@ import { getIconsData, getIconSlug, slugToVariableName } from '../sdk.mjs';
 import * as simpleIcons from '../index.mjs';
 import { testIcon } from './test-icon.js';
 
-(async () => {
-  const icons = await getIconsData();
+for (const icon of await getIconsData()) {
+  const slug = getIconSlug(icon);
+  const variableName = slugToVariableName(slug);
+  const subject = simpleIcons[variableName];
 
-  icons.map((icon) => {
-    const slug = getIconSlug(icon);
-    const variableName = slugToVariableName(slug);
-    const subject = simpleIcons[variableName];
-
-    testIcon(icon, subject, slug);
-  });
-})();
+  testIcon(icon, subject, slug);
+}
diff --git a/tests/test-icon.js b/tests/test-icon.js
index 87861984..c3ade33d 100644
--- a/tests/test-icon.js
+++ b/tests/test-icon.js
@@ -1,15 +1,28 @@
-import fs from 'node:fs';
+import fs from 'node:fs/promises';
 import path from 'node:path';
 import { strict as assert } from 'node:assert';
 import { describe, it } from 'mocha';
-import { URL_REGEX, titleToSlug } from '../sdk.mjs';
+import {
+  SVG_PATH_REGEX,
+  URL_REGEX,
+  getDirnameFromImportMeta,
+  titleToSlug,
+} from '../sdk.mjs';
 
-const iconsDir = path.resolve(process.cwd(), 'icons');
+const iconsDir = path.resolve(
+  getDirnameFromImportMeta(import.meta.url),
+  '..',
+  'icons',
+);
+
+/**
+ * @typedef {import('..').SimpleIcon} SimpleIcon
+ */
 
 /**
  * Checks if icon data matches a subject icon.
- * @param {import('..').SimpleIcon} icon Icon data
- * @param {import('..').SimpleIcon} subject Icon to check against icon data
+ * @param {SimpleIcon} icon Icon data
+ * @param {SimpleIcon} subject Icon to check against icon data
  * @param {String} slug Icon data slug
  */
 export const testIcon = (icon, subject, slug) => {
@@ -38,7 +51,7 @@ export const testIcon = (icon, subject, slug) => {
     });
 
     it('has a valid "path" value', () => {
-      assert.match(subject.path, /^[MmZzLlHhVvCcSsQqTtAaEe0-9-,.\s]+$/g);
+      assert.match(subject.path, SVG_PATH_REGEX);
     });
 
     it(`has ${icon.guidelines ? 'the correct' : 'no'} "guidelines"`, () => {
@@ -62,8 +75,8 @@ export const testIcon = (icon, subject, slug) => {
       }
     });
 
-    it('has a valid svg value', () => {
-      const svgFileContents = fs.readFileSync(svgPath, 'utf8');
+    it('has a valid svg value', async () => {
+      const svgFileContents = await fs.readFile(svgPath, 'utf8');
       assert.equal(subject.svg, svgFileContents);
     });