@blueprintjs/icons
Version:
Components, fonts, icons, and css files for creating and displaying icons.
91 lines (80 loc) • 3.76 kB
JavaScript
/*
* Copyright 2021 Palantir Technologies, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Generates SVG paths used in <Icon> React components
* N.B. we expect ../src/generated/ to contain SVG definitions of all the icons already
*/
const { pascalCase } = require("change-case");
const fs = require("fs");
const path = require("path");
// Note: we had issues with this approach using svgo v2.x, so for now we stick with v1.x
// With v2.x, some shapes within the icon SVGs would not get converted to paths correctly,
// resulting in invalid d="..." attributes rendered by the <Icon> component.
const SVGO = require("svgo");
/**
* @typedef {Object} IconMetadata
* @property {string} displayName - "Icon name" for display
* @property {string} iconName - `icon-name` for IconName and CSS class
* @property {string} tags - comma separated list of tags describing this icon
* @property {string} group - group to which this icon belongs
* @property {string} content - unicode character for icon glyph in font
*/
/** @type {IconMetadata[]} */
const ICONS_METADATA = require("../icons.json").sort((a, b) => a.iconName.localeCompare(b.iconName));
const { RESOURCES_DIR, writeLinesToFile } = require("./common");
const svgo = new SVGO({ plugins: [{ convertShapeToPath: { convertArcs: true } }] });
const ICON_NAMES = ICONS_METADATA.map(icon => icon.iconName);
(async () => {
for (const iconSize of [16, 20]) {
const iconPaths = await getIconPaths(iconSize);
for (const [iconName, pathStrings] of Object.entries(iconPaths)) {
const line =
pathStrings.length > 0
? `export default [${pathStrings.join(", ")}];`
: // special case for "blank" icon - we need an explicit typedef
`const p: string[] = []; export default p;`;
writeLinesToFile(`${iconSize}px/paths/${iconName}.ts`, line);
}
console.info(`Writing index file for ${iconSize}px icon kit paths...`);
writeLinesToFile(
`${iconSize}px/paths/index.ts`,
...ICON_NAMES.map(iconName => `export { default as ${pascalCase(iconName)} } from "./${iconName}";`),
);
console.info("Done.");
}
})();
/**
* Loads SVG file for each icon, extracts path strings `d="path-string"`,
* and constructs map of icon name to array of path strings.
*
* @param {16 | 20} iconSize
*/
async function getIconPaths(iconSize) {
/** @type Record<string, string[]> */
const iconPaths = {};
for (const iconName of ICON_NAMES) {
const filepath = path.join(RESOURCES_DIR, `${iconSize}px/${iconName}.svg`);
const svg = fs.readFileSync(filepath, "utf-8");
const optimizedSvg = await svgo.optimize(svg, { path: filepath });
const pathStrings = (optimizedSvg.data.match(/ d="[^"]+"/g) || [])
// strip off leading 'd="'
.map(s => s.slice(3))
// strip out newlines and tabs, but keep other whitespace
.map(s => s.replace(/[\n\t]/g, ""));
iconPaths[iconName] = pathStrings;
}
console.info(`Parsed ${Object.keys(iconPaths).length} ${iconSize}px icons.`);
return iconPaths;
}