mirror of
				https://github.com/Mibew/simple-icons.git
				synced 2025-11-04 12:25:08 +03:00 
			
		
		
		
	Refactor tests and scripts (#9237)
Co-authored-by: LitoMore <LitoMore@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									8abcd9c8b9
								
							
						
					
					
						commit
						17ea889273
					
				@ -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,
 | 
			
		||||
              '',
 | 
			
		||||
            ),
 | 
			
		||||
 | 
			
		||||
@ -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",
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
@ -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>';`;
 | 
			
		||||
 | 
			
		||||
@ -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) {
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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();
 | 
			
		||||
 | 
			
		||||
@ -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 */';
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
@ -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');
 | 
			
		||||
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);
 | 
			
		||||
  }
 | 
			
		||||
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 nIcons = (await getIconsData()).length;
 | 
			
		||||
  const newNIcons = overNIconsInReadme + updateRange;
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
})();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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',
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								sdk.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								sdk.d.ts
									
									
									
									
										vendored
									
									
								
							@ -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;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										16
									
								
								sdk.mjs
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								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: {
 | 
			
		||||
 | 
			
		||||
@ -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.`,
 | 
			
		||||
      );
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user