2021-02-08 18:14:31 +02:00
|
|
|
/**
|
|
|
|
* @fileoverview
|
|
|
|
* Some common utilities for scripts.
|
|
|
|
*/
|
|
|
|
|
2021-12-25 16:22:56 +02:00
|
|
|
import path from 'node:path';
|
2022-09-25 03:04:58 +02:00
|
|
|
import fs from 'node:fs/promises';
|
2021-12-25 16:22:56 +02:00
|
|
|
import { fileURLToPath } from 'node:url';
|
2021-02-22 15:15:37 +02:00
|
|
|
|
2022-01-19 16:34:08 +02:00
|
|
|
const TITLE_TO_SLUG_REPLACEMENTS = {
|
|
|
|
'+': 'plus',
|
|
|
|
'.': 'dot',
|
|
|
|
'&': 'and',
|
|
|
|
đ: 'd',
|
|
|
|
ħ: 'h',
|
|
|
|
ı: 'i',
|
|
|
|
ĸ: 'k',
|
|
|
|
ŀ: 'l',
|
|
|
|
ł: 'l',
|
|
|
|
ß: 'ss',
|
|
|
|
ŧ: 't',
|
|
|
|
};
|
|
|
|
|
|
|
|
const TITLE_TO_SLUG_CHARS_REGEX = RegExp(
|
|
|
|
`[${Object.keys(TITLE_TO_SLUG_REPLACEMENTS).join('')}]`,
|
|
|
|
'g',
|
|
|
|
);
|
|
|
|
|
|
|
|
const TITLE_TO_SLUG_RANGE_REGEX = /[^a-z0-9]/g;
|
|
|
|
|
2022-09-24 17:37:03 +02:00
|
|
|
export const URL_REGEX = /^https:\/\/[^\s]+$/;
|
|
|
|
|
2021-12-25 16:22:56 +02:00
|
|
|
/**
|
|
|
|
* Get the slug/filename for an icon.
|
|
|
|
* @param {Object} icon The icon data as it appears in _data/simple-icons.json
|
|
|
|
*/
|
|
|
|
export const getIconSlug = (icon) => icon.slug || titleToSlug(icon.title);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Extract the path from an icon SVG content.
|
|
|
|
* @param {Object} svg The icon SVG content
|
|
|
|
**/
|
|
|
|
export const svgToPath = (svg) => svg.match(/<path\s+d="([^"]*)/)[1];
|
2021-11-06 17:03:37 +02:00
|
|
|
|
2021-12-25 16:22:56 +02:00
|
|
|
/**
|
|
|
|
* Converts a brand title into a slug/filename.
|
|
|
|
* @param {String} title The title to convert
|
|
|
|
*/
|
|
|
|
export const titleToSlug = (title) =>
|
|
|
|
title
|
|
|
|
.toLowerCase()
|
2022-01-19 16:34:08 +02:00
|
|
|
.replace(
|
|
|
|
TITLE_TO_SLUG_CHARS_REGEX,
|
|
|
|
(char) => TITLE_TO_SLUG_REPLACEMENTS[char],
|
|
|
|
)
|
2021-12-25 16:22:56 +02:00
|
|
|
.normalize('NFD')
|
2022-01-19 16:34:08 +02:00
|
|
|
.replace(TITLE_TO_SLUG_RANGE_REGEX, '');
|
2019-07-03 23:33:03 +02:00
|
|
|
|
2021-12-25 16:22:56 +02:00
|
|
|
/**
|
|
|
|
* Converts a slug into a variable name that can be exported.
|
|
|
|
* @param {String} slug The slug to convert
|
|
|
|
*/
|
|
|
|
export const slugToVariableName = (slug) => {
|
|
|
|
const slugFirstLetter = slug[0].toUpperCase();
|
|
|
|
const slugRest = slug.slice(1);
|
|
|
|
return `si${slugFirstLetter}${slugRest}`;
|
|
|
|
};
|
2021-11-06 17:03:37 +02:00
|
|
|
|
2021-12-25 16:22:56 +02:00
|
|
|
/**
|
|
|
|
* Converts a brand title (as it is seen in simple-icons.json) into a brand
|
|
|
|
* title in HTML/SVG friendly format.
|
|
|
|
* @param {String} brandTitle The title to convert
|
|
|
|
*/
|
|
|
|
export const titleToHtmlFriendly = (brandTitle) =>
|
|
|
|
brandTitle
|
|
|
|
.replace(/&/g, '&')
|
|
|
|
.replace(/"/g, '"')
|
|
|
|
.replace(/</g, '<')
|
|
|
|
.replace(/>/g, '>')
|
|
|
|
.replace(/./g, (char) => {
|
|
|
|
const charCode = char.charCodeAt(0);
|
|
|
|
return charCode > 127 ? `&#${charCode};` : char;
|
|
|
|
});
|
2021-11-08 12:55:47 +02:00
|
|
|
|
2021-12-25 16:22:56 +02:00
|
|
|
/**
|
|
|
|
* Converts a brand title in HTML/SVG friendly format into a brand title (as
|
|
|
|
* it is seen in simple-icons.json)
|
|
|
|
* @param {String} htmlFriendlyTitle The title to convert
|
|
|
|
*/
|
|
|
|
export const htmlFriendlyToTitle = (htmlFriendlyTitle) =>
|
|
|
|
htmlFriendlyTitle
|
|
|
|
.replace(/&#([0-9]+);/g, (_, num) => String.fromCharCode(parseInt(num)))
|
|
|
|
.replace(
|
|
|
|
/&(quot|amp|lt|gt);/g,
|
|
|
|
(_, ref) => ({ quot: '"', amp: '&', lt: '<', gt: '>' }[ref]),
|
|
|
|
);
|
|
|
|
|
|
|
|
/**
|
2022-09-25 03:04:58 +02:00
|
|
|
* Get path of _data/simpe-icons.json.
|
2022-05-08 13:48:32 +02:00
|
|
|
* @param {String|undefined} rootDir Path to the root directory of the project.
|
2021-12-25 16:22:56 +02:00
|
|
|
*/
|
2022-09-25 03:04:58 +02:00
|
|
|
export const getIconDataPath = (rootDir) => {
|
2022-05-08 13:48:32 +02:00
|
|
|
if (rootDir === undefined) {
|
|
|
|
rootDir = path.resolve(getDirnameFromImportMeta(import.meta.url), '..');
|
|
|
|
}
|
2022-09-25 03:04:58 +02:00
|
|
|
return path.resolve(rootDir, '_data', 'simple-icons.json');
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get contents of _data/simple-icons.json.
|
|
|
|
* @param {String|undefined} rootDir Path to the root directory of the project.
|
|
|
|
*/
|
|
|
|
export const getIconsDataString = (rootDir) => {
|
|
|
|
return fs.readFile(getIconDataPath(rootDir), 'utf8');
|
2021-10-25 21:13:10 +02:00
|
|
|
};
|
2021-12-25 16:22:56 +02:00
|
|
|
|
|
|
|
/**
|
2022-05-08 13:48:32 +02:00
|
|
|
* Get icons data as object from _data/simple-icons.json.
|
|
|
|
* @param {String|undefined} rootDir Path to the root directory of the project.
|
2021-12-25 16:22:56 +02:00
|
|
|
*/
|
2022-05-08 13:48:32 +02:00
|
|
|
export const getIconsData = async (rootDir) => {
|
|
|
|
const fileContents = await getIconsDataString(rootDir);
|
2021-12-25 16:22:56 +02:00
|
|
|
return JSON.parse(fileContents).icons;
|
|
|
|
};
|
|
|
|
|
2022-09-25 03:04:58 +02:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
export const writeIconsData = async (iconsData, rootDir) => {
|
|
|
|
return fs.writeFile(
|
|
|
|
getIconDataPath(rootDir),
|
|
|
|
`${JSON.stringify(iconsData, null, 4)}\n`,
|
|
|
|
'utf8',
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2021-12-25 16:22:56 +02:00
|
|
|
/**
|
2022-05-08 13:48:32 +02:00
|
|
|
* Get the directory name where this file is located from `import.meta.url`,
|
|
|
|
* equivalent to the `__dirname` global variable in CommonJS.
|
2021-12-25 16:22:56 +02:00
|
|
|
* @param {String} importMetaUrl import.meta.url
|
|
|
|
*/
|
|
|
|
export const getDirnameFromImportMeta = (importMetaUrl) =>
|
|
|
|
path.dirname(fileURLToPath(importMetaUrl));
|
2022-01-31 00:09:44 +02:00
|
|
|
|
2022-03-31 14:30:30 +02:00
|
|
|
/**
|
|
|
|
* Replace Windows newline characters by Unix ones.
|
|
|
|
* @param {String} text The text to replace
|
|
|
|
*/
|
|
|
|
export const normalizeNewlines = (text) => {
|
|
|
|
return text.replace(/\r\n/g, '\n');
|
|
|
|
};
|
|
|
|
|
2022-09-25 03:04:58 +02:00
|
|
|
/**
|
|
|
|
* Convert non-6-digit hex color to 6-digit.
|
|
|
|
* @param {String} text The color text
|
|
|
|
*/
|
|
|
|
export const normalizeColor = (text) => {
|
|
|
|
let color = text.replace('#', '').toUpperCase();
|
|
|
|
if (color.length < 6) {
|
|
|
|
color = [...color.slice(0, 3)].map((x) => x.repeat(2)).join('');
|
|
|
|
} else if (color.length > 6) {
|
|
|
|
color = color.slice(0, 6);
|
|
|
|
}
|
|
|
|
return color;
|
|
|
|
};
|
|
|
|
|
2022-01-31 00:09:44 +02:00
|
|
|
/**
|
|
|
|
* Get information about third party extensions.
|
2022-04-30 16:04:46 +02:00
|
|
|
* @param {String} readmePath Path to the README file
|
2022-01-31 00:09:44 +02:00
|
|
|
*/
|
2022-04-30 16:04:46 +02:00
|
|
|
export const getThirdPartyExtensions = async (readmePath) =>
|
|
|
|
normalizeNewlines(await fs.readFile(readmePath, 'utf8'))
|
2022-01-31 00:09:44 +02:00
|
|
|
.split('## Third-Party Extensions\n\n')[1]
|
|
|
|
.split('\n\n')[0]
|
|
|
|
.split('\n')
|
|
|
|
.slice(2)
|
|
|
|
.map((line) => {
|
|
|
|
const [module, author] = line.split(' | ');
|
|
|
|
return {
|
|
|
|
module: {
|
|
|
|
name: /\[(.+)\]/.exec(module)[1],
|
|
|
|
url: /\((.+)\)/.exec(module)[1],
|
|
|
|
},
|
|
|
|
author: {
|
|
|
|
name: /\[(.+)\]/.exec(author)[1],
|
|
|
|
url: /\((.+)\)/.exec(author)[1],
|
|
|
|
},
|
|
|
|
};
|
|
|
|
});
|
2022-09-24 17:29:43 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* `Intl.Collator` object ready to be used for icon titles sorting.
|
|
|
|
* @type {Intl.Collator}
|
|
|
|
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator
|
|
|
|
**/
|
|
|
|
export const collator = new Intl.Collator('en', {
|
|
|
|
usage: 'search',
|
|
|
|
caseFirst: 'upper',
|
|
|
|
});
|