diff --git a/.jsonschema.json b/.jsonschema.json index 2bc5ac875..bcdf2eede 100644 --- a/.jsonschema.json +++ b/.jsonschema.json @@ -599,9 +599,8 @@ "required": ["type", "url"], "properties": { "type": { - "description": "The license name or 'custom'", - "type": "string", - "enum": ["custom"] + "description": "Custom license type", + "const": "custom" }, "url": { "description": "The URL to the license text by the brand", @@ -617,7 +616,7 @@ }, "duplicate": { "$id": "#duplicate", - "description": "A \"dup\" brand", + "description": "A brand that uses the same icon", "type": "object", "required": ["title"], "properties": { diff --git a/.npmignore b/.npmignore index c97285f70..65b188954 100644 --- a/.npmignore +++ b/.npmignore @@ -15,3 +15,4 @@ !sdk.mjs !sdk.js !sdk.d.ts +!.jsonschema.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 92e057a01..31199a16d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -458,7 +458,7 @@ A SDK is included in the `simple-icons/sdk` entrypoint of the npm package to mak ```typescript import { getIconsData, type IconData } from 'simple-icons/sdk'; -const iconsData: IconData[] = getIconsData(); +const iconsData: Promise = getIconsData(); ``` ```javascript @@ -466,6 +466,6 @@ import { getIconsData } from 'simple-icons/sdk'; /* @typedef {import("./simple-icons/sdk").IconData} IconData */ -/* @type {IconData[]} */ +/* @type {Promise} */ const iconsData = getIconsData(); ``` diff --git a/scripts/add-icon-data.js b/scripts/add-icon-data.js index 6c5f39009..9ee694d31 100644 --- a/scripts/add-icon-data.js +++ b/scripts/add-icon-data.js @@ -7,11 +7,11 @@ import {search} from 'fast-fuzzy'; import getRelativeLuminance from 'get-relative-luminance'; import autocomplete from 'inquirer-autocomplete-standalone'; import { - URL_REGEX, collator, getIconsDataString, normalizeColor, titleToSlug, + urlRegex, } from '../sdk.mjs'; import {getJsonSchemaData, writeIconsData} from './utils.js'; @@ -30,8 +30,10 @@ const licenseTypes = (license) => ({name: license, value: license}), ); -const isValidURL = (input) => - URL_REGEX.test(input) || 'Must be a valid and secure (https://) URL.'; +const isValidURL = async (input) => { + const regex = await urlRegex(); + return regex.test(input) || 'Must be a valid and secure (https://) URL.'; +}; const isValidHexColor = (input) => HEX_REGEX.test(input) || 'Must be a valid hex code.'; diff --git a/sdk.d.ts b/sdk.d.ts index 8fab23ee7..184fe2283 100644 --- a/sdk.d.ts +++ b/sdk.d.ts @@ -63,9 +63,9 @@ export type IconData = { /* The next code is autogenerated from sdk.mjs */ /* eslint-disable */ -export const URL_REGEX: RegExp; export const SVG_PATH_REGEX: RegExp; export function getDirnameFromImportMeta(importMetaUrl: string): string; +export function urlRegex(jsonschemaPath?: string): Promise; export function getIconSlug(icon: IconData): string; export function svgToPath(svg: string): string; export function titleToSlug(title: string): string; diff --git a/sdk.mjs b/sdk.mjs index 617ebdca5..0fa791075 100644 --- a/sdk.mjs +++ b/sdk.mjs @@ -33,11 +33,6 @@ const TITLE_TO_SLUG_CHARS_REGEX = new RegExp( const TITLE_TO_SLUG_RANGE_REGEX = /[^a-z\d]/g; -/** - * Regex to validate HTTPs URLs. - */ -export const URL_REGEX = /^https:\/\/[^\s"']+$/; - /** * Regex to validate SVG paths. */ @@ -52,6 +47,24 @@ export const SVG_PATH_REGEX = /^m[-mzlhvcsqtae\d,. ]+$/i; export const getDirnameFromImportMeta = (importMetaUrl) => path.dirname(fileURLToPath(importMetaUrl)); +/** + * Build a regex to validate HTTPs URLs. + * @param {String} jsonschemaPath Path to the *.jsonschema.json* file + * @returns {Promise} Regex to validate HTTPs URLs + */ +export const urlRegex = async ( + jsonschemaPath = path.join( + getDirnameFromImportMeta(import.meta.url), + '.jsonschema.json', + ), +) => { + return new RegExp( + JSON.parse( + await fs.readFile(jsonschemaPath, 'utf8'), + ).definitions.url.pattern, + ); +}; + /** * Get the slug/filename for an icon. * @param {IconData} icon The icon data as it appears in *_data/simple-icons.json* diff --git a/svglint.config.mjs b/svglint.config.mjs index f6eeee742..b1db78392 100644 --- a/svglint.config.mjs +++ b/svglint.config.mjs @@ -9,11 +9,11 @@ import { SVG_PATH_REGEX, collator, getDirnameFromImportMeta, + getIconsData, htmlFriendlyToTitle, } from './sdk.mjs'; const __dirname = getDirnameFromImportMeta(import.meta.url); -const dataFile = path.join(__dirname, '_data', 'simple-icons.json'); const htmlNamedEntitiesFile = path.join( __dirname, 'node_modules', @@ -22,7 +22,7 @@ const htmlNamedEntitiesFile = path.join( ); const svglintIgnoredFile = path.join(__dirname, '.svglint-ignored.json'); -const data = JSON.parse(await fs.readFile(dataFile, 'utf8')); +const icons = await getIconsData(); const htmlNamedEntities = JSON.parse( await fs.readFile(htmlNamedEntitiesFile, 'utf8'), ); @@ -369,9 +369,7 @@ const config = { if (_validCodepointsRepr) { const iconName = htmlFriendlyToTitle(iconTitleText); - const iconExists = data.icons.some( - (icon) => icon.title === iconName, - ); + const iconExists = icons.some((icon) => icon.title === iconName); if (!iconExists) { reporter.error( `No icon with title "${iconName}" found in simple-icons.json`, diff --git a/tests/test-icon.js b/tests/test-icon.js index 1097e6b36..409e13812 100644 --- a/tests/test-icon.js +++ b/tests/test-icon.js @@ -4,7 +4,6 @@ import path from 'node:path'; import {describe, it} from 'mocha'; import { SVG_PATH_REGEX, - URL_REGEX, getDirnameFromImportMeta, titleToSlug, } from '../sdk.mjs'; @@ -43,7 +42,6 @@ export const testIcon = (icon, subject, slug) => { it('has the correct "source"', () => { assert.equal(subject.source, icon.source); - assert.match(subject.source, URL_REGEX); }); it('has an "svg" value', () => { @@ -67,8 +65,6 @@ export const testIcon = (icon, subject, slug) => { assert.equal(subject.license.type, icon.license.type); if (icon.license.type === 'custom') { assert.equal(subject.license.url, icon.license.url); - } else { - assert.match(subject.license.url, URL_REGEX); } } else { assert.equal(subject.license, undefined);