Expose SDK in NPM package (#8077)

This commit is contained in:
Álvaro Mondéjar 2023-04-19 15:23:13 +02:00 committed by GitHub
parent 64c1996878
commit 6178243859
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 395 additions and 225 deletions

View File

@ -6,3 +6,4 @@ npm-debug.log
/index.js /index.js
/index.mjs /index.mjs
/index.d.ts /index.d.ts
/sdk.js

3
.gitignore vendored
View File

@ -2,6 +2,7 @@
/index.js /index.js
/index.mjs /index.mjs
/index.d.ts /index.d.ts
/sdk.js
# Ignore all files in the icons folder # Ignore all files in the icons folder
icons/* icons/*
@ -21,6 +22,8 @@ yarn.lock
# Dependency directories # Dependency directories
node_modules/ node_modules/
# Generated files
*.tgz
### macOS ### ### macOS ###
# General # General

View File

@ -12,3 +12,6 @@
!index.mjs !index.mjs
!index.d.ts !index.d.ts
!types.d.ts !types.d.ts
!sdk.mjs
!sdk.js
!sdk.d.ts

View File

@ -13,3 +13,4 @@ scripts/build/templates/*.js
index.js index.js
index.mjs index.mjs
index.d.ts index.d.ts
sdk.js

View File

@ -4,7 +4,7 @@ import {
getDirnameFromImportMeta, getDirnameFromImportMeta,
htmlFriendlyToTitle, htmlFriendlyToTitle,
collator, collator,
} from './scripts/utils.js'; } from './sdk.mjs';
import svgpath from 'svgpath'; import svgpath from 'svgpath';
import svgPathBbox from 'svg-path-bbox'; import svgPathBbox from 'svg-path-bbox';
import parsePath from 'svg-path-segments'; import parsePath from 'svg-path-segments';

View File

@ -396,3 +396,22 @@ Then, start a Docker container for simple-icons and attach to it:
```shell ```shell
docker run -it --rm --entrypoint "/bin/ash" simple-icons docker run -it --rm --entrypoint "/bin/ash" simple-icons
``` ```
## Developing Third-Party Extensions
A SDK is included in the `simple-icons/sdk` entrypoint of the npm package to make it easier the development of third party extensions with Javascript and Typescript.
```typescript
import { getIconsData, type IconData } from 'simple-icons/sdk';
const iconsData: IconData[] = getIconsData();
```
```javascript
import { getIconsData } from 'simple-icons/sdk';
/* @typedef {import("./simple-icons/sdk").IconData} IconData */
/* @type {IconData[]} */
const iconsData = getIconsData();
```

View File

@ -25,7 +25,25 @@
}, },
"./icons/*": [ "./icons/*": [
"./icons/*" "./icons/*"
] ],
"./sdk": {
"import": {
"types": "./sdk.d.ts",
"default": "./sdk.mjs"
},
"module": {
"types": "./sdk.d.ts",
"default": "./sdk.mjs"
},
"require": {
"types": "./sdk.d.ts",
"default": "./sdk.js"
},
"default": {
"types": "./sdk.d.ts",
"default": "./sdk.js"
}
}
}, },
"sideEffects": false, "sideEffects": false,
"repository": { "repository": {

View File

@ -1,4 +1,3 @@
import fs from 'node:fs/promises';
import inquirer from 'inquirer'; import inquirer from 'inquirer';
import chalk from 'chalk'; import chalk from 'chalk';
import getRelativeLuminance from 'get-relative-luminance'; import getRelativeLuminance from 'get-relative-luminance';
@ -7,11 +6,10 @@ import {
collator, collator,
getJsonSchemaData, getJsonSchemaData,
getIconsDataString, getIconsDataString,
getIconDataPath,
writeIconsData,
titleToSlug, titleToSlug,
normalizeColor, normalizeColor,
} from './utils.js'; } from '../sdk.mjs';
import { writeIconsData } from './utils.js';
const hexPattern = /^#?[a-f0-9]{3,8}$/i; const hexPattern = /^#?[a-f0-9]{3,8}$/i;

View File

@ -1,10 +1,7 @@
#!/usr/bin/env node #!/usr/bin/env node
/** /**
* @fileoverview * @fileoverview
* Compiles our icons into static .js files that can be imported in the browser * Simple Icons package build script.
* and are tree-shakeable. The static .js files go in icons/{filename}.js. Also
* generates an index.js that exports all icons by title, but is not
* tree-shakeable
*/ */
import { promises as fs } from 'node:fs'; import { promises as fs } from 'node:fs';
@ -19,7 +16,7 @@ import {
getIconsData, getIconsData,
getDirnameFromImportMeta, getDirnameFromImportMeta,
collator, collator,
} from '../utils.js'; } from '../../sdk.mjs';
const __dirname = getDirnameFromImportMeta(import.meta.url); const __dirname = getDirnameFromImportMeta(import.meta.url);
@ -29,6 +26,8 @@ const rootDir = path.resolve(__dirname, '..', '..');
const iconsDir = path.resolve(rootDir, 'icons'); const iconsDir = path.resolve(rootDir, 'icons');
const indexJsFile = path.resolve(rootDir, 'index.js'); const indexJsFile = path.resolve(rootDir, 'index.js');
const indexMjsFile = path.resolve(rootDir, 'index.mjs'); const indexMjsFile = path.resolve(rootDir, 'index.mjs');
const sdkJsFile = path.resolve(rootDir, 'sdk.js');
const sdkMjsFile = path.resolve(rootDir, 'sdk.mjs');
const indexDtsFile = path.resolve(rootDir, 'index.d.ts'); const indexDtsFile = path.resolve(rootDir, 'index.d.ts');
const templatesDir = path.resolve(__dirname, 'templates'); const templatesDir = path.resolve(__dirname, 'templates');
@ -68,10 +67,9 @@ const build = async () => {
licenseToObject(icon.license), licenseToObject(icon.license),
); );
}; };
const writeJs = async (filepath, rawJavaScript) => { const writeJs = async (filepath, rawJavaScript, opts = null) => {
const { code } = await esbuildTransform(rawJavaScript, { opts = opts === null ? { minify: true } : opts;
minify: true, const { code } = await esbuildTransform(rawJavaScript, opts);
});
await fs.writeFile(filepath, code); await fs.writeFile(filepath, code);
}; };
const writeTs = async (filepath, rawTypeScript) => { const writeTs = async (filepath, rawTypeScript) => {
@ -119,6 +117,11 @@ const build = async () => {
'', '',
)}`; )}`;
await writeTs(indexDtsFile, rawIndexDts); await writeTs(indexDtsFile, rawIndexDts);
// create a CommonJS SDK file
await writeJs(sdkJsFile, await fs.readFile(sdkMjsFile, UTF8), {
format: 'cjs',
});
}; };
build(); build();

View File

@ -5,7 +5,7 @@
* icon SVG filename to standard output. * icon SVG filename to standard output.
*/ */
import { titleToSlug } from './utils.js'; import { titleToSlug } from '../sdk.mjs';
if (process.argv.length < 3) { if (process.argv.length < 3) {
console.error('Provide a brand name as argument'); console.error('Provide a brand name as argument');

View File

@ -4,14 +4,10 @@
* CLI tool to run jsonschema on the simple-icons.json data file. * CLI tool to run jsonschema on the simple-icons.json data file.
*/ */
import { promises as fs } from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { Validator } from 'jsonschema'; import { Validator } from 'jsonschema';
import { import { getDirnameFromImportMeta, getIconsData } from '../../sdk.mjs';
getDirnameFromImportMeta, import { getJsonSchemaData } from '../utils.js';
getIconsData,
getJsonSchemaData,
} from '../utils.js';
const icons = await getIconsData(); const icons = await getIconsData();
const __dirname = getDirnameFromImportMeta(import.meta.url); const __dirname = getDirnameFromImportMeta(import.meta.url);

View File

@ -6,7 +6,7 @@
*/ */
import fakeDiff from 'fake-diff'; import fakeDiff from 'fake-diff';
import { getIconsDataString, normalizeNewlines, collator } from '../utils.js'; import { getIconsDataString, normalizeNewlines, collator } from '../../sdk.mjs';
/** /**
* Contains our tests so they can be isolated from each other. * Contains our tests so they can be isolated from each other.

View File

@ -6,7 +6,7 @@
import fs from 'node:fs'; import fs from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { getDirnameFromImportMeta } from '../utils.js'; import { getDirnameFromImportMeta } from '../../sdk.mjs';
const __dirname = getDirnameFromImportMeta(import.meta.url); const __dirname = getDirnameFromImportMeta(import.meta.url);

View File

@ -7,7 +7,7 @@
import fs from 'node:fs'; import fs from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { getDirnameFromImportMeta } from '../utils.js'; import { getDirnameFromImportMeta } from '../../sdk.mjs';
const __dirname = getDirnameFromImportMeta(import.meta.url); const __dirname = getDirnameFromImportMeta(import.meta.url);

View File

@ -7,7 +7,7 @@
import { promises as fs } from 'node:fs'; import { promises as fs } from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { getIconsData, getIconSlug } from '../utils.js'; import { getIconsData, getIconSlug } from '../../sdk.mjs';
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename); const __dirname = path.dirname(__filename);

View File

@ -7,7 +7,7 @@
*/ */
import { promises as fs } from 'node:fs'; import { promises as fs } from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { getDirnameFromImportMeta, getIconsData } from '../utils.js'; import { getDirnameFromImportMeta, getIconsData } from '../../sdk.mjs';
const regexMatcher = /Over\s(\d+)\s/; const regexMatcher = /Over\s(\d+)\s/;
const updateRange = 100; const updateRange = 100;

View File

@ -1,110 +1,9 @@
/**
* @fileoverview
* Some common utilities for scripts.
*/
import path from 'node:path'; import path from 'node:path';
import fs from 'node:fs/promises'; import fs from 'node:fs/promises';
import { fileURLToPath } from 'node:url'; import { getDirnameFromImportMeta, getIconDataPath } from '../sdk.mjs';
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;
export const URL_REGEX = /^https:\/\/[^\s]+$/;
/**
* Get the directory name where this file is located from `import.meta.url`,
* equivalent to the `__dirname` global variable in CommonJS.
* @param {String} importMetaUrl import.meta.url
*/
export const getDirnameFromImportMeta = (importMetaUrl) =>
path.dirname(fileURLToPath(importMetaUrl));
const __dirname = getDirnameFromImportMeta(import.meta.url); const __dirname = getDirnameFromImportMeta(import.meta.url);
/**
* 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];
/**
* Converts a brand title into a slug/filename.
* @param {String} title The title to convert
*/
export const titleToSlug = (title) =>
title
.toLowerCase()
.replace(
TITLE_TO_SLUG_CHARS_REGEX,
(char) => TITLE_TO_SLUG_REPLACEMENTS[char],
)
.normalize('NFD')
.replace(TITLE_TO_SLUG_RANGE_REGEX, '');
/**
* 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}`;
};
/**
* 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, '&amp;')
.replace(/"/g, '&quot;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/./g, (char) => {
const charCode = char.charCodeAt(0);
return charCode > 127 ? `&#${charCode};` : char;
});
/**
* 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]),
);
/** /**
* Get JSON schema data. * Get JSON schema data.
* @param {String|undefined} rootDir Path to the root directory of the project. * @param {String|undefined} rootDir Path to the root directory of the project.
@ -112,104 +11,23 @@ export const htmlFriendlyToTitle = (htmlFriendlyTitle) =>
export const getJsonSchemaData = async ( export const getJsonSchemaData = async (
rootDir = path.resolve(__dirname, '..'), rootDir = path.resolve(__dirname, '..'),
) => { ) => {
const __dirname = getDirnameFromImportMeta(import.meta.url);
const jsonSchemaPath = path.resolve(rootDir, '.jsonschema.json'); const jsonSchemaPath = path.resolve(rootDir, '.jsonschema.json');
const jsonSchemaString = await fs.readFile(jsonSchemaPath, 'utf8'); const jsonSchemaString = await fs.readFile(jsonSchemaPath, 'utf8');
return JSON.parse(jsonSchemaString); return JSON.parse(jsonSchemaString);
}; };
/**
* Get path of _data/simpe-icons.json.
* @param {String|undefined} rootDir Path to the root directory of the project.
*/
export const getIconDataPath = (
rootDir = path.resolve(getDirnameFromImportMeta(import.meta.url), '..'),
) => {
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');
};
/**
* Get icons data as object from _data/simple-icons.json.
* @param {String|undefined} rootDir Path to the root directory of the project.
*/
export const getIconsData = async (rootDir) => {
const fileContents = await getIconsDataString(rootDir);
return JSON.parse(fileContents).icons;
};
/** /**
* Write icons data to _data/simple-icons.json. * Write icons data to _data/simple-icons.json.
* @param {Object} iconsData Icons data object. * @param {Object} iconsData Icons data object.
* @param {String|undefined} rootDir Path to the root directory of the project. * @param {String|undefined} rootDir Path to the root directory of the project.
*/ */
export const writeIconsData = async (iconsData, rootDir) => { export const writeIconsData = async (
iconsData,
rootDir = path.resolve(__dirname, '..'),
) => {
return fs.writeFile( return fs.writeFile(
getIconDataPath(rootDir), getIconDataPath(rootDir),
`${JSON.stringify(iconsData, null, 4)}\n`, `${JSON.stringify(iconsData, null, 4)}\n`,
'utf8', 'utf8',
); );
}; };
/**
* 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');
};
/**
* 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;
};
/**
* Get information about third party extensions.
* @param {String} readmePath Path to the README file
*/
export const getThirdPartyExtensions = async (readmePath) =>
normalizeNewlines(await fs.readFile(readmePath, 'utf8'))
.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.split('<picture>')[0])[1],
},
author: {
name: /\[(.+)\]/.exec(author)[1],
url: /\((.+)\)/.exec(author)[1],
},
};
});
/**
* `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',
});

96
sdk.d.ts vendored Normal file
View File

@ -0,0 +1,96 @@
/**
* @fileoverview
* Types for Simple Icons SDK.
*/
/**
* The data for a third-party extension.
*
* Includes the module and author of the extension,
* both including a name and URL.
*
* @see {@link https://github.com/simple-icons/simple-icons#third-party-extensions Third-Party Extensions}
*/
export type ThirdPartyExtension = {
module: ThirdPartyExtensionSubject;
author: ThirdPartyExtensionSubject;
};
type ThirdPartyExtensionSubject = {
name: string;
url: string;
};
/**
* The license for a Simple Icon.
*
* Corresponds to the `license` property in the *_data/simple-icons.json* file.
*
* @see {@link https://github.com/simple-icons/simple-icons/blob/develop/CONTRIBUTING.md#optional-data Optional Data}
*/
export type License = SPDXLicense | CustomLicense;
type SPDXLicense = {
type: string;
url?: string;
};
type CustomLicense = {
type: 'custom';
url: string;
};
/**
* The aliases for a Simple Icon.
*
* Corresponds to the `aliases` property in the *_data/simple-icons.json* file.
*
* @see {@link https://github.com/simple-icons/simple-icons/blob/develop/CONTRIBUTING.md#aliases Aliases}
*/
export type Aliases = {
aka?: string[];
dup?: DuplicateAlias[];
loc?: { [key: string]: string };
};
type DuplicateAlias = {
title: string;
hex?: string;
guidelines?: string;
};
/**
* The data for a Simple Icon.
*
* Corresponds to the data stored for each icon in the *_data/simple-icons.json* file.
*
* @see {@link https://github.com/mondeja/simple-icons/blob/utils-entrypoint/CONTRIBUTING.md#7-update-the-json-data-for-simpleiconsorg Update the JSON Data for SimpleIcons.org}
*/
export type IconData = {
title: string;
hex: string;
source: string;
slug?: string;
guidelines?: string;
license?: License;
aliases?: Aliases;
};
export const URL_REGEX: RegExp;
export function getDirnameFromImportMeta(importMetaUrl: string): string;
export function getIconSlug(icon: IconData): string;
export function svgToPath(svg: string): string;
export function titleToSlug(title: string): string;
export function slugToVariableName(slug: string): string;
export function titleToHtmlFriendly(brandTitle: string): string;
export function htmlFriendlyToTitle(htmlFriendlyTitle: string): string;
export function getIconDataPath(rootDir?: string): string;
export function getIconsDataString(rootDir?: string): string;
export function getIconsData(rootDir?: string): IconData[];
export function normalizeNewlines(text: string): string;
export function normalizeColor(text: string): string;
export function getThirdPartyExtensions(
readmePath?: string,
): Promise<ThirdPartyExtension[]>;
export const collator: Intl.Collator;

222
sdk.mjs Normal file
View File

@ -0,0 +1,222 @@
/**
* @fileoverview
* Simple Icons SDK.
*/
import path from 'node:path';
import fs from 'node:fs/promises';
import { fileURLToPath } from 'node:url';
/**
* @typedef {import("./sdk").ThirdPartyExtension} ThirdPartyExtension
* @typedef {import("./sdk").IconData} IconData
*/
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;
/**
* Regex to validate HTTPs URLs.
*/
export const URL_REGEX = /^https:\/\/[^\s]+$/;
/**
* Get the directory name where this file is located from `import.meta.url`,
* equivalent to the `__dirname` global variable in CommonJS.
* @param {String} importMetaUrl import.meta.url
* @returns {String} Directory name in which this file is located
*/
export const getDirnameFromImportMeta = (importMetaUrl) =>
path.dirname(fileURLToPath(importMetaUrl));
/**
* Get the slug/filename for an icon.
* @param {IconData} icon The icon data as it appears in *_data/simple-icons.json*
* @returns {String} The slug/filename for the icon
*/
export const getIconSlug = (icon) => icon.slug || titleToSlug(icon.title);
/**
* Extract the path from an icon SVG content.
* @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];
/**
* Converts a brand title into a slug/filename.
* @param {String} title The title to convert
* @returns {String} The slug/filename for the title
*/
export const titleToSlug = (title) =>
title
.toLowerCase()
.replace(
TITLE_TO_SLUG_CHARS_REGEX,
(char) => TITLE_TO_SLUG_REPLACEMENTS[char],
)
.normalize('NFD')
.replace(TITLE_TO_SLUG_RANGE_REGEX, '');
/**
* Converts a slug into a variable name that can be exported.
* @param {String} slug The slug to convert
* @returns {String} The variable name for the slug
*/
export const slugToVariableName = (slug) => {
const slugFirstLetter = slug[0].toUpperCase();
const slugRest = slug.slice(1);
return `si${slugFirstLetter}${slugRest}`;
};
/**
* Converts a brand title as defined in *_data/simple-icons.json* into a brand
* title in HTML/SVG friendly format.
* @param {String} brandTitle The title to convert
* @returns {String} The brand title in HTML/SVG friendly format
*/
export const titleToHtmlFriendly = (brandTitle) =>
brandTitle
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/./g, (char) => {
const charCode = char.charCodeAt(0);
return charCode > 127 ? `&#${charCode};` : char;
});
/**
* Converts a brand title in HTML/SVG friendly format into a brand title (as
* it is seen in *_data/simple-icons.json*)
* @param {String} htmlFriendlyTitle The title to convert
* @returns {String} The brand title in HTML/SVG friendly format
*/
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]),
);
/**
* Get path of *_data/simpe-icons.json*.
* @param {String|undefined} rootDir Path to the root directory of the project
* @returns {String} Path of *_data/simple-icons.json*
*/
export const getIconDataPath = (
rootDir = getDirnameFromImportMeta(import.meta.url),
) => {
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
* @returns {String} Content of *_data/simple-icons.json*
*/
export const getIconsDataString = (
rootDir = getDirnameFromImportMeta(import.meta.url),
) => {
return fs.readFile(getIconDataPath(rootDir), 'utf8');
};
/**
* Get icons data as object from *_data/simple-icons.json*.
* @param {String|undefined} rootDir Path to the root directory of the project
* @returns {IconData[]} Icons data as array from *_data/simple-icons.json*
*/
export const getIconsData = async (
rootDir = getDirnameFromImportMeta(import.meta.url),
) => {
const fileContents = await getIconsDataString(rootDir);
return JSON.parse(fileContents).icons;
};
/**
* Replace Windows newline characters by Unix ones.
* @param {String} text The text to replace
* @returns {String} The text with Windows newline characters replaced by Unix ones
*/
export const normalizeNewlines = (text) => {
return text.replace(/\r\n/g, '\n');
};
/**
* Convert non-6-digit hex color to 6-digit with the character `#` stripped.
* @param {String} text The color text
* @returns {String} The color text in 6-digit hex format
*/
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;
};
/**
* Get information about third party extensions from the README table.
* @param {String|undefined} readmePath Path to the README file
* @returns {Promise<ThirdPartyExtension[]>} Information about third party extensions
*/
export const getThirdPartyExtensions = async (
readmePath = path.join(
getDirnameFromImportMeta(import.meta.url),
'README.md',
),
) =>
normalizeNewlines(await fs.readFile(readmePath, 'utf8'))
.split('## Third-Party Extensions\n\n')[1]
.split('\n\n')[0]
.split('\n')
.slice(2)
.map((line) => {
let [module, author] = line.split(' | ');
// README shipped with package has not Github theme image links
module = module.includes('<picture>')
? module.split('<picture>')[0]
: module.split('<img src="')[1].split(' ').slice(5).join(' ');
return {
module: {
name: /\[(.+)\]/.exec(module)[1],
url: /\((.+)\)/.exec(module)[1],
},
author: {
name: /\[(.+)\]/.exec(author)[1],
url: /\((.+)\)/.exec(author)[1],
},
};
});
/**
* `Intl.Collator` object ready to be used for icon titles sorting.
* @type {Intl.Collator}
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator Intl.Collator}
**/
export const collator = new Intl.Collator('en', {
usage: 'search',
caseFirst: 'upper',
});

View File

@ -2,11 +2,7 @@ import fs from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { describe, test } from 'mocha'; import { describe, test } from 'mocha';
import { strict as assert } from 'node:assert'; import { strict as assert } from 'node:assert';
import { import { getThirdPartyExtensions, getDirnameFromImportMeta } from '../sdk.mjs';
getThirdPartyExtensions,
getDirnameFromImportMeta,
URL_REGEX,
} from '../scripts/utils.js';
const __dirname = getDirnameFromImportMeta(import.meta.url); const __dirname = getDirnameFromImportMeta(import.meta.url);
const root = path.dirname(__dirname); const root = path.dirname(__dirname);

View File

@ -1,8 +1,4 @@
import { import { getIconsData, getIconSlug, slugToVariableName } from '../sdk.mjs';
getIconsData,
getIconSlug,
slugToVariableName,
} from '../scripts/utils.js';
import * as simpleIcons from '../index.mjs'; import * as simpleIcons from '../index.mjs';
import { testIcon } from './test-icon.js'; import { testIcon } from './test-icon.js';

View File

@ -2,7 +2,7 @@ import fs from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { strict as assert } from 'node:assert'; import { strict as assert } from 'node:assert';
import { describe, it } from 'mocha'; import { describe, it } from 'mocha';
import { URL_REGEX, titleToSlug } from '../scripts/utils.js'; import { URL_REGEX, titleToSlug } from '../sdk.mjs';
const iconsDir = path.resolve(process.cwd(), 'icons'); const iconsDir = path.resolve(process.cwd(), 'icons');