mirror of
https://github.com/lucide-icons/lucide.git
synced 2025-12-17 11:07:40 +01:00
Compare commits
23 Commits
v0.93.0
...
v0.1.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d349be0fee | ||
|
|
c7a8ea09d9 | ||
|
|
6fb9593f28 | ||
|
|
2f16a80e6d | ||
|
|
4bdee5a5a2 | ||
|
|
41b9d7637d | ||
|
|
d6b50f942a | ||
|
|
3a44f64098 | ||
|
|
6a0f52daab | ||
|
|
e38ad92be1 | ||
|
|
70b178215b | ||
|
|
bdc4f81aa5 | ||
|
|
018a3af0bd | ||
|
|
c9551388d0 | ||
|
|
4365dddf57 | ||
|
|
ea8bec9320 | ||
|
|
e08eff7dee | ||
|
|
4ac9e0bc37 | ||
|
|
446fd3f852 | ||
|
|
e732f4dd6d | ||
|
|
771cf1d66e | ||
|
|
eee84b0edf | ||
|
|
de6906e242 |
11
.editorconfig
Normal file
11
.editorconfig
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
quote_type = single
|
||||||
|
max_line_length = 100
|
||||||
4
.eslintignore
Normal file
4
.eslintignore
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
dist
|
||||||
|
build
|
||||||
|
coverage
|
||||||
|
lib
|
||||||
21
.eslintrc.json
Normal file
21
.eslintrc.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": ["airbnb-base", "prettier"],
|
||||||
|
"plugins": ["import", "prettier"],
|
||||||
|
"rules": {
|
||||||
|
"no-console": "off",
|
||||||
|
"no-param-reassign": "off",
|
||||||
|
"no-shadow": "off",
|
||||||
|
"no-use-before-define": "off",
|
||||||
|
"prettier/prettier": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "all"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,6 +1,10 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
|
build
|
||||||
|
lib
|
||||||
sandbox
|
sandbox
|
||||||
stash
|
stash
|
||||||
coverage
|
coverage
|
||||||
|
stats
|
||||||
|
*.log
|
||||||
|
|||||||
52
README.md
52
README.md
@@ -13,12 +13,64 @@ Featherity is a fork of [Feather Icons](https://github.com/feathericons/feather)
|
|||||||
* [Contributing](#contributing)
|
* [Contributing](#contributing)
|
||||||
* [License](#license)
|
* [License](#license)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
``` bash
|
||||||
|
npm install featherity
|
||||||
|
#or
|
||||||
|
yarn add featherity
|
||||||
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
At its core, Featherity is a collection of [SVG](https://svgontheweb.com/#svg) files. This means that you can use Feather icons in all the same ways you can use SVGs (e.g. `img`, `background-image`, `inline`, `object`, `embed`, `iframe`). Here's a helpful article detailing the many ways SVGs can be used on the web: [SVG on the Web – Implementation Options](https://svgontheweb.com/#implementation)
|
At its core, Featherity is a collection of [SVG](https://svgontheweb.com/#svg) files. This means that you can use Feather icons in all the same ways you can use SVGs (e.g. `img`, `background-image`, `inline`, `object`, `embed`, `iframe`). Here's a helpful article detailing the many ways SVGs can be used on the web: [SVG on the Web – Implementation Options](https://svgontheweb.com/#implementation)
|
||||||
|
|
||||||
The following are additional ways you can use Featherity.
|
The following are additional ways you can use Featherity.
|
||||||
|
|
||||||
|
### ESModule
|
||||||
|
|
||||||
|
``` js
|
||||||
|
import { Camera } from 'featherity';
|
||||||
|
// Returns HTMLElement
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
document.appendChild(Camera);
|
||||||
|
```
|
||||||
|
|
||||||
|
### React
|
||||||
|
|
||||||
|
``` js
|
||||||
|
import { Camera } from 'featherity/react';
|
||||||
|
// Returns ReactComponent
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
const App = () => {
|
||||||
|
return <Camera color="red" size={48}/>
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Vue
|
||||||
|
|
||||||
|
``` vue
|
||||||
|
<template>
|
||||||
|
<div id="app">
|
||||||
|
<Camera color="red" :size="48"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Camera } from 'featherity/vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "App",
|
||||||
|
components: {
|
||||||
|
Camera
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
### Figma
|
### Figma
|
||||||
|
|
||||||
You can use the components from [this Figma file](https://www.figma.com/file/g0UipfQlRfGrntKPxZknM7/Featherity).
|
You can use the components from [this Figma file](https://www.figma.com/file/g0UipfQlRfGrntKPxZknM7/Featherity).
|
||||||
|
|||||||
28
babel.config.js
Normal file
28
babel.config.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
'@babel/env',
|
||||||
|
{
|
||||||
|
loose: true,
|
||||||
|
modules: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
// plugins: ['babel-plugin-add-import-extension'],
|
||||||
|
env: {
|
||||||
|
test: {
|
||||||
|
presets: ['@babel/env'],
|
||||||
|
plugins: ['@babel/plugin-transform-runtime'],
|
||||||
|
},
|
||||||
|
dev: {
|
||||||
|
plugins: [
|
||||||
|
[
|
||||||
|
'transform-inline-environment-variables',
|
||||||
|
{
|
||||||
|
include: ['NODE_ENV'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
60
package.json
60
package.json
@@ -1,9 +1,61 @@
|
|||||||
{
|
{
|
||||||
|
"name": "featherity",
|
||||||
|
"amdName": "featherity",
|
||||||
|
"license": "ISC",
|
||||||
|
"version": "0.1.0-alpha.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"source": "build/featherity.js",
|
||||||
|
"main": "dist/cjs/featherity.js",
|
||||||
|
"main:umd": "dist/umd/featherity.js",
|
||||||
|
"module": "lib/featherity.js",
|
||||||
|
"unpkg": "dist/umd/featherity.min.js",
|
||||||
|
"sideEffects": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"react:compile": "yarn workspace react compile"
|
"start": "babel-watch --watch src",
|
||||||
|
"clean": "rimraf lib && rimraf dist && rimraf build",
|
||||||
|
"build": "yarn clean && yarn build:transpile && yarn build:icons && yarn build:es && yarn build:esbrowser && yarn build:bundles",
|
||||||
|
"build:transpile": "babel src -d build",
|
||||||
|
"build:icons": "npx babel-node ./scripts/buildIcons.js --presets @babel/env",
|
||||||
|
"build:es": "babel build -d lib --source-maps --ignore '**/*.test.js','**/__mocks__'",
|
||||||
|
"build:esbrowser": "BROWSER_COMPAT=true yarn build:es -d dist/esm",
|
||||||
|
"build:bundles": "rollup -c rollup.config.js",
|
||||||
|
"optimize": "npx babel-node ./scripts/optimizeSvgs.js --presets @babel/env"
|
||||||
},
|
},
|
||||||
"workspaces": [
|
"devDependencies": {
|
||||||
"packages/react"
|
"@ampproject/rollup-plugin-closure-compiler": "^0.25.2",
|
||||||
]
|
"@atomico/rollup-plugin-sizes": "^1.1.4",
|
||||||
|
"@babel/cli": "^7.10.5",
|
||||||
|
"@babel/core": "^7.11.1",
|
||||||
|
"@babel/node": "^7.10.5",
|
||||||
|
"@babel/preset-env": "^7.11.0",
|
||||||
|
"@rollup/plugin-babel": "^5.0.0",
|
||||||
|
"@rollup/plugin-replace": "^2.3.2",
|
||||||
|
"babel-plugin-add-import-extension": "^1.4.3",
|
||||||
|
"cheerio": "^1.0.0-rc.2",
|
||||||
|
"eslint": "^4.19.1",
|
||||||
|
"eslint-config-airbnb-base": "^12.1.0",
|
||||||
|
"eslint-config-prettier": "^2.9.0",
|
||||||
|
"eslint-plugin-import": "^2.5.0",
|
||||||
|
"eslint-plugin-prettier": "^2.5.0",
|
||||||
|
"html-minifier": "^3.5.8",
|
||||||
|
"lint-staged": "^6.0.0",
|
||||||
|
"microbundle": "^0.12.3",
|
||||||
|
"prettier": "^1.8.2",
|
||||||
|
"rollup": "^2.7.3",
|
||||||
|
"rollup-plugin-commonjs": "^10.1.0",
|
||||||
|
"rollup-plugin-flow-entry": "^0.3.3",
|
||||||
|
"rollup-plugin-license": "^2.0.0",
|
||||||
|
"rollup-plugin-node-resolve": "^5.2.0",
|
||||||
|
"rollup-plugin-terser": "^5.2.0",
|
||||||
|
"rollup-plugin-visualizer": "^4.1.0",
|
||||||
|
"semantic-release": "^12.2.2",
|
||||||
|
"svgo": "^1.3.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"core-js": "3",
|
||||||
|
"htmlparser2": "^4.1.0",
|
||||||
|
"lodash": "^4.17.19",
|
||||||
|
"prop-types": "^15.7.2",
|
||||||
|
"react": "^16.13.1"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
61
rollup.config.js
Normal file
61
rollup.config.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import babel from '@rollup/plugin-babel';
|
||||||
|
import replace from '@rollup/plugin-replace';
|
||||||
|
import bundleSize from '@atomico/rollup-plugin-sizes';
|
||||||
|
import compiler from '@ampproject/rollup-plugin-closure-compiler';
|
||||||
|
import { terser } from 'rollup-plugin-terser';
|
||||||
|
import visualizer from 'rollup-plugin-visualizer';
|
||||||
|
import license from 'rollup-plugin-license';
|
||||||
|
import resolve from 'rollup-plugin-node-resolve';
|
||||||
|
import commonJS from 'rollup-plugin-commonjs';
|
||||||
|
import pkg from './package.json';
|
||||||
|
|
||||||
|
const outputFileName = pkg.name;
|
||||||
|
|
||||||
|
const inputs = ['build/featherity.js'];
|
||||||
|
const bundles = [
|
||||||
|
{ inputs, format: 'umd', dir: 'dist', minify: true },
|
||||||
|
{ inputs, format: 'umd', dir: 'dist' },
|
||||||
|
{ inputs, format: 'cjs', dir: 'dist' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const configs = bundles
|
||||||
|
.map(({ inputs, dir, format, minify }) =>
|
||||||
|
inputs.map(input => ({
|
||||||
|
input,
|
||||||
|
external: ['lodash/camelCase', 'lodash/upperFirst'],
|
||||||
|
plugins: [
|
||||||
|
format === 'umd' &&
|
||||||
|
replace({
|
||||||
|
__DEV__: minify ? 'false' : 'true',
|
||||||
|
}),
|
||||||
|
babel({ babelHelpers: 'bundled' }),
|
||||||
|
// The two minifiers together seem to procude a smaller bundle 🤷♂️
|
||||||
|
minify && compiler(),
|
||||||
|
minify && terser(),
|
||||||
|
license({ banner: `${pkg.name} v${pkg.version} - ${pkg.license}` }),
|
||||||
|
bundleSize(),
|
||||||
|
resolve(),
|
||||||
|
commonJS({
|
||||||
|
include: 'node_modules/**',
|
||||||
|
}),
|
||||||
|
visualizer({
|
||||||
|
sourcemap: true,
|
||||||
|
filename: `stats/${outputFileName}${minify ? '-min' : ''}.html`,
|
||||||
|
}),
|
||||||
|
].filter(Boolean),
|
||||||
|
output: {
|
||||||
|
name: 'featherity',
|
||||||
|
file: `${dir}/${format}/${outputFileName}${minify ? '.min' : ''}.js`,
|
||||||
|
format,
|
||||||
|
sourcemap: true,
|
||||||
|
exports: 'named',
|
||||||
|
globals: {
|
||||||
|
'lodash/camelCase': 'camelCase',
|
||||||
|
'lodash/upperFirst': 'upperFirst',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
.flat();
|
||||||
|
|
||||||
|
export default configs;
|
||||||
23
scripts/build/generateExportsFile.js
Normal file
23
scripts/build/generateExportsFile.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import { generateComponentName, resetFile, writeFile, readFile } from './helpers';
|
||||||
|
|
||||||
|
export default function(inputEntry, outputDirectory, componentGetter, iconNodes) {
|
||||||
|
const fileName = path.basename(inputEntry);
|
||||||
|
|
||||||
|
// Reset file
|
||||||
|
resetFile(fileName, outputDirectory);
|
||||||
|
|
||||||
|
const icons = Object.keys(iconNodes);
|
||||||
|
|
||||||
|
// Generate Import for Icon VNodes
|
||||||
|
icons.forEach(iconName => {
|
||||||
|
const componentName = generateComponentName(iconName);
|
||||||
|
const importString = `export { default as ${componentName} } from './${iconName}';\n`;
|
||||||
|
writeFile(importString, fileName, outputDirectory);
|
||||||
|
});
|
||||||
|
|
||||||
|
writeFile('\n', fileName, outputDirectory);
|
||||||
|
|
||||||
|
console.log(`Successfully generated ${fileName} file`);
|
||||||
|
}
|
||||||
27
scripts/build/generateIconFiles.js
Normal file
27
scripts/build/generateIconFiles.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/* eslint-disable import/no-extraneous-dependencies */
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import prettier from 'prettier';
|
||||||
|
import { generateComponentName } from './helpers';
|
||||||
|
|
||||||
|
export default function(iconNode, outputDirectory, template) {
|
||||||
|
const icons = Object.keys(iconNode);
|
||||||
|
const iconsDistDirectory = path.join(outputDirectory, `icons`);
|
||||||
|
|
||||||
|
if (!fs.existsSync(iconsDistDirectory)) {
|
||||||
|
fs.mkdirSync(iconsDistDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
icons.forEach(icon => {
|
||||||
|
const location = path.join(iconsDistDirectory, `${icon}.js`);
|
||||||
|
const componentName = generateComponentName(icon);
|
||||||
|
|
||||||
|
const node = JSON.stringify(iconNode[icon]);
|
||||||
|
|
||||||
|
const elementTemplate = template({ componentName, node });
|
||||||
|
|
||||||
|
fs.writeFileSync(location, prettier.format(elementTemplate, { parser: 'babel' }), 'utf-8');
|
||||||
|
|
||||||
|
console.log('Successfully built', componentName);
|
||||||
|
});
|
||||||
|
}
|
||||||
37
scripts/build/helpers.js
Normal file
37
scripts/build/helpers.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { upperFirst, camelCase } from 'lodash/string';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a componentName of a String.
|
||||||
|
*
|
||||||
|
* @param {string} iconName
|
||||||
|
*/
|
||||||
|
export const generateComponentName = iconName =>
|
||||||
|
iconName === 'github' ? 'GitHub' : upperFirst(camelCase(iconName));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the file contents.
|
||||||
|
*
|
||||||
|
* @param {string} fileName
|
||||||
|
* @param {string} outputDirectory
|
||||||
|
*/
|
||||||
|
export const resetFile = (fileName, outputDirectory) =>
|
||||||
|
fs.writeFileSync(path.join(outputDirectory, fileName), '', 'utf-8');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the file contents.
|
||||||
|
*
|
||||||
|
* @param {string} path
|
||||||
|
*/
|
||||||
|
export const readFile = entry => fs.readFileSync(path.resolve(__dirname, '../../', entry), 'utf-8');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* writes content to a file
|
||||||
|
*
|
||||||
|
* @param {string} content
|
||||||
|
* @param {string} fileName
|
||||||
|
* @param {string} outputDirectory
|
||||||
|
*/
|
||||||
|
export const writeFile = (content, fileName, outputDirectory) =>
|
||||||
|
fs.appendFileSync(path.join(outputDirectory, fileName), content, 'utf-8');
|
||||||
43
scripts/buildIcons.js
Normal file
43
scripts/buildIcons.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import renderIconsObject from './render/renderIconsObject';
|
||||||
|
import renderIconNodes from './render/renderIconNodes';
|
||||||
|
import generateIconFiles from './build/generateIconFiles';
|
||||||
|
import generateExportsFile from './build/generateExportsFile';
|
||||||
|
|
||||||
|
const IN_DIR = path.resolve(__dirname, '../icons');
|
||||||
|
const OUTPUT_DIR = path.resolve(__dirname, '../build');
|
||||||
|
const SRC_DIR = path.resolve(__dirname, '../src');
|
||||||
|
|
||||||
|
if (!fs.existsSync(OUTPUT_DIR)) {
|
||||||
|
fs.mkdirSync(OUTPUT_DIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
const svgFiles = fs.readdirSync(IN_DIR).filter(file => path.extname(file) === '.svg');
|
||||||
|
const getSvg = svgFile => fs.readFileSync(path.join(IN_DIR, svgFile));
|
||||||
|
|
||||||
|
const icons = renderIconsObject(svgFiles, getSvg);
|
||||||
|
const iconVNodes = renderIconNodes(icons);
|
||||||
|
|
||||||
|
// Generates iconsNodes files for each icon
|
||||||
|
generateIconFiles(
|
||||||
|
iconVNodes,
|
||||||
|
OUTPUT_DIR,
|
||||||
|
({ componentName, node }) => `
|
||||||
|
import createElement from '../../src/createElement';
|
||||||
|
|
||||||
|
const ${componentName} = ${node};
|
||||||
|
|
||||||
|
export const element = createElement('${componentName}', ${componentName});
|
||||||
|
export default ${componentName};
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Generates entry files for the compiler filled with icons exports
|
||||||
|
generateExportsFile(
|
||||||
|
path.join(SRC_DIR, 'icons/index.js'),
|
||||||
|
path.join(OUTPUT_DIR, 'icons'),
|
||||||
|
'getElement',
|
||||||
|
iconVNodes,
|
||||||
|
);
|
||||||
17
scripts/optimizeSvgs.js
Normal file
17
scripts/optimizeSvgs.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import processSvg from './render/processSvg';
|
||||||
|
|
||||||
|
const ICONS_DIR = path.resolve(__dirname, '../icons');
|
||||||
|
|
||||||
|
console.log(`Optimizing SVGs in ${ICONS_DIR}...`);
|
||||||
|
|
||||||
|
const svgFiles = fs.readdirSync(ICONS_DIR).filter(file => path.extname(file) === '.svg');
|
||||||
|
console.log(svgFiles);
|
||||||
|
|
||||||
|
svgFiles
|
||||||
|
.filter(file => path.extname(file) === '.svg')
|
||||||
|
.forEach(svgFile => {
|
||||||
|
const svg = fs.readFileSync(path.join(ICONS_DIR, svgFile));
|
||||||
|
processSvg(svg).then(svg => fs.writeFileSync(path.join(ICONS_DIR, svgFile), svg));
|
||||||
|
});
|
||||||
11
scripts/render/default-attrs.json
Normal file
11
scripts/render/default-attrs.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"xmlns": "http://www.w3.org/2000/svg",
|
||||||
|
"width": 24,
|
||||||
|
"height": 24,
|
||||||
|
"viewBox": "0 0 24 24",
|
||||||
|
"fill": "none",
|
||||||
|
"stroke": "currentColor",
|
||||||
|
"stroke-width": 2,
|
||||||
|
"stroke-linecap": "round",
|
||||||
|
"stroke-linejoin": "round"
|
||||||
|
}
|
||||||
57
scripts/render/processSvg.js
Normal file
57
scripts/render/processSvg.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
/* eslint-disable import/no-extraneous-dependencies */
|
||||||
|
import Svgo from 'svgo';
|
||||||
|
import cheerio from 'cheerio';
|
||||||
|
import { format } from 'prettier';
|
||||||
|
|
||||||
|
import DEFAULT_ATTRS from './default-attrs.json';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process SVG string.
|
||||||
|
* @param {string} svg - An SVG string.
|
||||||
|
* @param {Promise<string>}
|
||||||
|
*/
|
||||||
|
function processSvg(svg) {
|
||||||
|
return (
|
||||||
|
optimize(svg)
|
||||||
|
.then(setAttrs)
|
||||||
|
.then(format)
|
||||||
|
// remove semicolon inserted by prettier
|
||||||
|
// because prettier thinks it's formatting JSX not HTML
|
||||||
|
.then(svg => svg.replace(/;/g, ''))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optimize SVG with `svgo`.
|
||||||
|
* @param {string} svg - An SVG string.
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
function optimize(svg) {
|
||||||
|
const svgo = new Svgo({
|
||||||
|
plugins: [
|
||||||
|
{ convertShapeToPath: false },
|
||||||
|
{ mergePaths: false },
|
||||||
|
{ removeAttrs: { attrs: '(fill|stroke.*)' } },
|
||||||
|
{ removeTitle: true },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
svgo.optimize(svg, ({ data }) => resolve(data));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set default attibutes on SVG.
|
||||||
|
* @param {string} svg - An SVG string.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function setAttrs(svg) {
|
||||||
|
const $ = cheerio.load(svg);
|
||||||
|
|
||||||
|
Object.keys(DEFAULT_ATTRS).forEach(key => $('svg').attr(key, DEFAULT_ATTRS[key]));
|
||||||
|
|
||||||
|
return $('body').html();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default processSvg;
|
||||||
29
scripts/render/renderIconNodes.js
Normal file
29
scripts/render/renderIconNodes.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/* eslint-disable import/no-extraneous-dependencies */
|
||||||
|
import { parseDOM } from 'htmlparser2';
|
||||||
|
import DEFAULT_ATTRS from './default-attrs.json';
|
||||||
|
|
||||||
|
export default iconsObject => {
|
||||||
|
const iconNodes = {};
|
||||||
|
|
||||||
|
Object.keys(iconsObject).forEach(icon => {
|
||||||
|
const svgString = iconsObject[icon];
|
||||||
|
const dom = parseDOM(svgString);
|
||||||
|
|
||||||
|
const children = dom.map(element => [
|
||||||
|
element.name,
|
||||||
|
{
|
||||||
|
...element.attribs,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
iconNodes[icon] = [
|
||||||
|
'svg',
|
||||||
|
{
|
||||||
|
...DEFAULT_ATTRS,
|
||||||
|
},
|
||||||
|
children,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
return iconNodes;
|
||||||
|
};
|
||||||
34
scripts/render/renderIconsObject.js
Normal file
34
scripts/render/renderIconsObject.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/* eslint-disable import/no-extraneous-dependencies */
|
||||||
|
import path from 'path';
|
||||||
|
import cheerio from 'cheerio';
|
||||||
|
import { minify } from 'html-minifier';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get contents between opening and closing `<svg>` tags.
|
||||||
|
* @param {string} svg
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function getSvgContents(svg) {
|
||||||
|
const $ = cheerio.load(svg);
|
||||||
|
|
||||||
|
return minify($('svg').html(), { collapseWhitespace: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an object in the format: `{ <name>: <contents> }`.
|
||||||
|
* @param {string[]} svgFiles - A list of filenames.
|
||||||
|
* @param {Function} getSvg - A function that returns the contents of an SVG file given a filename.
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
export default (svgFiles, getSvg) =>
|
||||||
|
svgFiles
|
||||||
|
.map(svgFile => {
|
||||||
|
const name = path.basename(svgFile, '.svg');
|
||||||
|
const svg = getSvg(svgFile);
|
||||||
|
const contents = getSvgContents(svg);
|
||||||
|
return { name, contents };
|
||||||
|
})
|
||||||
|
.reduce((icons, icon) => {
|
||||||
|
icons[icon.name] = icon.contents;
|
||||||
|
return icons;
|
||||||
|
}, {});
|
||||||
19
src/createElement.js
Normal file
19
src/createElement.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
const createElement = (tag, attrs, children = []) => {
|
||||||
|
const element = document.createElement(tag);
|
||||||
|
|
||||||
|
Object.keys(attrs).forEach(name => {
|
||||||
|
element.setAttribute(name, attrs[name]);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (children.length) {
|
||||||
|
children = children.forEach(child => {
|
||||||
|
const childElement = createElement(...child);
|
||||||
|
|
||||||
|
element.appendChild(childElement);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return element;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ([tag, attrs, children]) => createElement(tag, attrs, children);
|
||||||
5
src/icons/index.js
Normal file
5
src/icons/index.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
/*
|
||||||
|
Icons exports.
|
||||||
|
|
||||||
|
Will be generated
|
||||||
|
*/
|
||||||
40
src/replaceElement.js
Normal file
40
src/replaceElement.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import camelCase from 'lodash/camelCase';
|
||||||
|
import upperFirst from 'lodash/upperFirst';
|
||||||
|
import createElement from './createElement';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the attributes of an HTML element.
|
||||||
|
* @param {HTMLElement} element
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
function getAttrs(element) {
|
||||||
|
return Array.from(element.attributes).reduce((attrs, attr) => {
|
||||||
|
attrs[attr.name] = attr.value;
|
||||||
|
return attrs;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default (element, { nameAttr, icons, attrs }) => {
|
||||||
|
const iconName = element.getAttribute(nameAttr);
|
||||||
|
const ComponentName = upperFirst(camelCase(iconName));
|
||||||
|
|
||||||
|
const iconNode = icons[ComponentName];
|
||||||
|
|
||||||
|
if (!iconNode) {
|
||||||
|
return console.warn(
|
||||||
|
`${element.outerHTML} icon name was not found in the provided icons object.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [, iconAttrs] = iconNode;
|
||||||
|
|
||||||
|
iconNode[1] = {
|
||||||
|
...iconAttrs,
|
||||||
|
...getAttrs(element),
|
||||||
|
...attrs,
|
||||||
|
};
|
||||||
|
|
||||||
|
const svgElement = createElement(iconNode);
|
||||||
|
|
||||||
|
return element.parentNode.replaceChild(svgElement, element);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user