mirror of
https://github.com/Mibew/simple-icons.git
synced 2024-11-15 01:44:12 +03:00
Expose SDK in NPM package (#8077)
This commit is contained in:
parent
64c1996878
commit
6178243859
@ -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
3
.gitignore
vendored
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -13,3 +13,4 @@ scripts/build/templates/*.js
|
|||||||
index.js
|
index.js
|
||||||
index.mjs
|
index.mjs
|
||||||
index.d.ts
|
index.d.ts
|
||||||
|
sdk.js
|
||||||
|
@ -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';
|
||||||
|
@ -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();
|
||||||
|
```
|
||||||
|
20
package.json
20
package.json
@ -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": {
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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');
|
||||||
|
@ -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);
|
||||||
|
@ -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.
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
192
scripts/utils.js
192
scripts/utils.js
@ -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, '&')
|
|
||||||
.replace(/"/g, '"')
|
|
||||||
.replace(/</g, '<')
|
|
||||||
.replace(/>/g, '>')
|
|
||||||
.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
96
sdk.d.ts
vendored
Normal 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
222
sdk.mjs
Normal 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, '&')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.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',
|
||||||
|
});
|
@ -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);
|
||||||
|
@ -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';
|
||||||
|
|
||||||
|
@ -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');
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user