mirror of
https://github.com/lucide-icons/lucide.git
synced 2025-12-16 20:27:43 +01:00
Featherity Npm package, reorganize scripting. (#52)
* New setup for new NPM package * Add build scripts for dist * Add introduction readme * Refactor names * update package.json * remove log * rename variable * Factoring * Improve optimize script * Add eslint config * Eslint fixes * rename import * Move packeges * Setup rollup and build progress * Refactor scripts * fix lint error * remove lint disabler * Bring back old libraries * add indentation * reset packages directory * remove vscode setting files * 0.1.0-alpha.0 * new version * 0.1.0-alpha.1 * Fix build process * Add create element to the entry file * update version number * publish new alhpa version * fixing bugs * Add jest and tests * replace with XML createElement * set new version * Fix svg generation * Add tests for main library * Update docs * Adjust tests and selectors * update the spec * Update README.md * Update README.md * Update README.md * update version * Update README.md * Move function to helpers file * rename license, package and readme * Fix build files * rename packages Co-authored-by: Eric Fennis <eric.fennis@endurance.com>
This commit is contained in:
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
|
||||||
5
.eslintignore
Normal file
5
.eslintignore
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
dist
|
||||||
|
build
|
||||||
|
coverage
|
||||||
|
lib
|
||||||
|
tests
|
||||||
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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -2,3 +2,11 @@
|
|||||||
.next
|
.next
|
||||||
.now
|
.now
|
||||||
node_modules
|
node_modules
|
||||||
|
dist
|
||||||
|
build
|
||||||
|
lib
|
||||||
|
sandbox
|
||||||
|
stash
|
||||||
|
coverage
|
||||||
|
stats
|
||||||
|
*.log
|
||||||
|
|||||||
4
.npmignore
Normal file
4
.npmignore
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
.github
|
||||||
|
packages
|
||||||
|
stats
|
||||||
|
build
|
||||||
@@ -21,4 +21,4 @@ Guidelines for pull requests:
|
|||||||
|
|
||||||
Before creating an icon request, please search to see if someone has requested the icon already. If there is an open request, please add a :+1:.
|
Before creating an icon request, please search to see if someone has requested the icon already. If there is an open request, please add a :+1:.
|
||||||
|
|
||||||
If the icon has not already been requested, [create an issue](https://github.com/featherity/featherity/issues/new?title=Icon%20Request:) with a title of `Icon request: <icon name>` and add as much information as possible.
|
If the icon has not already been requested, [create an issue](https://github.com/lucide-icons/lucide/issues/new?title=Icon%20Request:) with a title of `Icon request: <icon name>` and add as much information as possible.
|
||||||
|
|||||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
ISC License
|
ISC License
|
||||||
|
|
||||||
Copyright (c) 2020, Featherity Contributors
|
Copyright (c) 2020, Lucide Contributors
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
|||||||
132
README.md
132
README.md
@@ -1,10 +1,10 @@
|
|||||||
# Featherity
|
# Lucide
|
||||||
|
|
||||||
[](https://discord.gg/EH6nSts)
|
[](https://discord.gg/EH6nSts)
|
||||||
|
|
||||||
## What is Featherity?
|
## What is Lucide?
|
||||||
|
|
||||||
Featherity is a community-run fork of [Feather Icons](https://github.com/feathericons/feather), open for anyone to contribute icons.
|
Lucide is a community-run fork of [Feather Icons](https://github.com/feathericons/feather), open for anyone to contribute icons.
|
||||||
|
|
||||||
Note that we are completely independent from Feather, so **icons submitted here won't get added to Feather Icons or its associated librairies**.
|
Note that we are completely independent from Feather, so **icons submitted here won't get added to Feather Icons or its associated librairies**.
|
||||||
|
|
||||||
@@ -15,11 +15,127 @@ Note that we are completely independent from Feather, so **icons submitted here
|
|||||||
* [Contributing](#contributing)
|
* [Contributing](#contributing)
|
||||||
* [License](#license)
|
* [License](#license)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Package Managers
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
npm install lucide
|
||||||
|
#or
|
||||||
|
yarn add lucide
|
||||||
|
```
|
||||||
|
|
||||||
|
### CDN
|
||||||
|
|
||||||
|
``` html
|
||||||
|
<!-- Development version -->
|
||||||
|
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.js"></script>
|
||||||
|
|
||||||
|
<!-- Production version -->
|
||||||
|
<script src="https://unpkg.com/lucide@latest"></script>
|
||||||
|
```
|
||||||
|
|
||||||
## 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, Lucide 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 Lucide.
|
||||||
|
With the Javascript library you can easily incorporate the icon you want in your webpage.
|
||||||
|
|
||||||
|
### With unpkg
|
||||||
|
|
||||||
|
Here is a complete example with unpkg
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<body>
|
||||||
|
<i icon-name="volume-2" class="my-class"></i>
|
||||||
|
<i icon-name="x"></i>
|
||||||
|
<i icon-name="menu"></i>
|
||||||
|
|
||||||
|
<script src="https://unpkg.com/lucide@latest"></script>
|
||||||
|
<script>
|
||||||
|
lucide.createIcons();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
```
|
||||||
|
|
||||||
|
### With ESModules
|
||||||
|
|
||||||
|
To reduce bundle size, lucide is build to be fully threeshakeble.
|
||||||
|
The `createIcons` function will search for HTMLElements with the attribute `icon-name` and replace it with the svg from the given icon name.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- Your HTML file -->
|
||||||
|
<i icon-name="menu"></i>
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { createIcons, icons } from 'lucide';
|
||||||
|
|
||||||
|
// Caustion, this will import all the icons and bundle them.
|
||||||
|
createIcons({icons});
|
||||||
|
|
||||||
|
// Recommended way, to include only the icons you need.
|
||||||
|
import { createIcons, Menu, ArrowRight, Globe } from 'lucide';
|
||||||
|
|
||||||
|
createIcons({
|
||||||
|
icons: {
|
||||||
|
Menu,
|
||||||
|
ArrowRight,
|
||||||
|
Globe,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Additional Options
|
||||||
|
|
||||||
|
In the `createIcons` function you can pass some extra parameters to adjust the `nameAttr` or add custom attributes like for example classes.
|
||||||
|
|
||||||
|
Here is a full example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { createIcons } from 'lucide';
|
||||||
|
|
||||||
|
createIcons({
|
||||||
|
attrs: {
|
||||||
|
class: ['my-custom-class', 'icon'],
|
||||||
|
'stroke-width': 1,
|
||||||
|
stroke: '#333',
|
||||||
|
},
|
||||||
|
nameAttr: 'icon-name', // atrribute for the icon name.
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Threeshake the library, only use the icons you use
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { createIcons, Menu, ArrowRight, Globe } from 'lucide';
|
||||||
|
|
||||||
|
createIcons({
|
||||||
|
icons: {
|
||||||
|
Menu,
|
||||||
|
ArrowRight,
|
||||||
|
Globe,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Custom Element binding
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { createElement, Menu } from 'lucide';
|
||||||
|
|
||||||
|
const menuIcon = createElement(Menu); // Returns HTMLElement (svg)
|
||||||
|
|
||||||
|
// set custom attributes with browser native functions
|
||||||
|
menuIcon.setAttribute('stroke', '#333');
|
||||||
|
menuIcon.classList.add('my-icon-class');
|
||||||
|
|
||||||
|
// Append HTMLElement in webpage
|
||||||
|
const myApp = document.getElementById('app');
|
||||||
|
myApp.appendChild(menuIcon);
|
||||||
|
```
|
||||||
|
|
||||||
### Figma
|
### Figma
|
||||||
|
|
||||||
@@ -27,9 +143,9 @@ You can use the components from [this Figma file](https://www.figma.com/file/g0U
|
|||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
For more info on how to contribute please see the [contribution guidelines](https://github.com/featherity/featherity/blob/master/CONTRIBUTING.md).
|
For more info on how to contribute please see the [contribution guidelines](https://github.com/lucide-icons/lucide/blob/master/CONTRIBUTING.md).
|
||||||
|
|
||||||
Caught a mistake or want to contribute to the documentation? [Edit this page on Github](https://github.com/featherity/featherity/blob/master/README.md)
|
Caught a mistake or want to contribute to the documentation? [Edit this page on Github](https://github.com/lucide-icons/lucide/blob/master/README.md)
|
||||||
|
|
||||||
## Community
|
## Community
|
||||||
|
|
||||||
@@ -37,4 +153,4 @@ Join the community on our [Discord](https://discord.gg/EH6nSts) server!
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Featherity is licensed under the [ISC License](https://github.com/featherity/featherity/blob/master/LICENSE).
|
Lucide is licensed under the [ISC License](https://github.com/lucide-icons/lucide/blob/master/LICENSE).
|
||||||
|
|||||||
27
babel.config.js
Normal file
27
babel.config.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
'@babel/env',
|
||||||
|
{
|
||||||
|
loose: true,
|
||||||
|
modules: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
env: {
|
||||||
|
test: {
|
||||||
|
presets: ['@babel/env'],
|
||||||
|
plugins: ['@babel/plugin-transform-runtime'],
|
||||||
|
},
|
||||||
|
dev: {
|
||||||
|
plugins: [
|
||||||
|
[
|
||||||
|
'transform-inline-environment-variables',
|
||||||
|
{
|
||||||
|
include: ['NODE_ENV'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
14
jest.config.js
Normal file
14
jest.config.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
const esModules = ['lodash-es'].join('|');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
verbose: true,
|
||||||
|
roots: ['<rootDir>/src/', '<rootDir>/tests/'],
|
||||||
|
moduleFileExtensions: ['js'],
|
||||||
|
transformIgnorePatterns: [`/node_modules/(?!${esModules})`],
|
||||||
|
moduleNameMapper: {
|
||||||
|
'^@/(.*)$': '<rootDir>/src/$1',
|
||||||
|
},
|
||||||
|
transform: {
|
||||||
|
'^.+\\.js$': 'babel-jest',
|
||||||
|
},
|
||||||
|
};
|
||||||
67
package.json
67
package.json
@@ -1,12 +1,63 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"name": "lucide",
|
||||||
|
"amdName": "lucide",
|
||||||
|
"homepage": "https://featherity.netlify.app",
|
||||||
|
"repository": "github:lucide-icons/lucide",
|
||||||
|
"url" : "https://github.com/owner/project/issues",
|
||||||
|
"license": "ISC",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"source": "build/lucide.js",
|
||||||
|
"main": "dist/cjs/lucide.js",
|
||||||
|
"main:umd": "dist/umd/lucide.js",
|
||||||
|
"module": "lib/lucide.js",
|
||||||
|
"unpkg": "dist/umd/lucide.min.js",
|
||||||
|
"sideEffects": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"react:compile": "yarn workspace react compile",
|
"start": "babel-watch --watch src",
|
||||||
"site:dev": "yarn workspace site dev",
|
"clean": "rimraf lib && rimraf dist && rimraf build",
|
||||||
"site:deploy": "yarn workspace site deploy"
|
"build": "yarn clean && yarn build:move && yarn build:icons && yarn build:es && yarn build:esbrowser && yarn build:bundles",
|
||||||
|
"build:move": "cp -av src 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": "BROWSER_COMPAT=true rollup -c rollup.config.js",
|
||||||
|
"optimize": "npx babel-node ./scripts/optimizeSvgs.js --presets @babel/env",
|
||||||
|
"test": "jest"
|
||||||
},
|
},
|
||||||
"workspaces": [
|
"devDependencies": {
|
||||||
"packages/react",
|
"@ampproject/rollup-plugin-closure-compiler": "^0.25.2",
|
||||||
"packages/site"
|
"@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",
|
||||||
|
"babel-jest": "^26.3.0",
|
||||||
|
"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",
|
||||||
|
"jest": "^26.4.2",
|
||||||
|
"lodash": "^4.17.19",
|
||||||
|
"prettier": "^1.8.2",
|
||||||
|
"rollup": "^2.7.3",
|
||||||
|
"rollup-plugin-commonjs": "^10.1.0",
|
||||||
|
"rollup-plugin-license": "^2.0.0",
|
||||||
|
"rollup-plugin-node-resolve": "^5.2.0",
|
||||||
|
"rollup-plugin-replace": "^2.2.0",
|
||||||
|
"rollup-plugin-terser": "^5.2.0",
|
||||||
|
"rollup-plugin-visualizer": "^4.1.0",
|
||||||
|
"svgo": "^1.3.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/plugin-transform-runtime": "^7.11.5",
|
||||||
|
"core-js": "3",
|
||||||
|
"htmlparser2": "^4.1.0",
|
||||||
|
"lodash-es": "^4.17.15",
|
||||||
|
"prop-types": "^15.7.2"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "react-featherity",
|
"name": "lucide-react",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "React component for Featherity icons",
|
"description": "React component for lucide icons",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"typings": "src/index.d.ts",
|
"typings": "src/index.d.ts",
|
||||||
"author": "John Letey",
|
"author": "John Letey",
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ const Layout = ({ children }) => {
|
|||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex justifyContent="center" alignItems="center">
|
<Flex justifyContent="center" alignItems="center">
|
||||||
<Link href="https://github.com/featherity/featherity" isExternal style={{ fontSize: "18px", marginRight: '24px' }}>
|
<Link href="https://github.com/lucide-icons/lucide" isExternal style={{ fontSize: "18px", marginRight: '24px' }}>
|
||||||
Github
|
Github
|
||||||
</Link>
|
</Link>
|
||||||
<div onClick={toggleColorMode} style={{ cursor: "pointer" }}>
|
<div onClick={toggleColorMode} style={{ cursor: "pointer" }}>
|
||||||
|
|||||||
77
rollup.config.js
Normal file
77
rollup.config.js
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import babel from '@rollup/plugin-babel';
|
||||||
|
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 replace from 'rollup-plugin-replace';
|
||||||
|
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/lucide.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: [
|
||||||
|
replace({
|
||||||
|
'icons = {}': 'icons = allIcons',
|
||||||
|
delimiters: ['', ''],
|
||||||
|
}),
|
||||||
|
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: 'lucide',
|
||||||
|
file: `${dir}/${format}/${outputFileName}${minify ? '.min' : ''}.js`,
|
||||||
|
format,
|
||||||
|
sourcemap: true,
|
||||||
|
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, appendFile } 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`;
|
||||||
|
appendFile(importString, fileName, outputDirectory);
|
||||||
|
});
|
||||||
|
|
||||||
|
appendFile('\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);
|
||||||
|
});
|
||||||
|
}
|
||||||
42
scripts/buildIcons.js
Normal file
42
scripts/buildIcons.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
import { readSvgDirectory } from './helpers';
|
||||||
|
|
||||||
|
const ICONS_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 = readSvgDirectory(ICONS_DIR);
|
||||||
|
|
||||||
|
const icons = renderIconsObject(svgFiles, ICONS_DIR);
|
||||||
|
|
||||||
|
const iconVNodes = renderIconNodes(icons);
|
||||||
|
|
||||||
|
// Generates iconsNodes files for each icon
|
||||||
|
generateIconFiles(
|
||||||
|
iconVNodes,
|
||||||
|
OUTPUT_DIR,
|
||||||
|
({ componentName, node }) => `
|
||||||
|
const ${componentName} = ${node};
|
||||||
|
|
||||||
|
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,
|
||||||
|
);
|
||||||
74
scripts/helpers.js
Normal file
74
scripts/helpers.js
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
|
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');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* append content to a file
|
||||||
|
*
|
||||||
|
* @param {string} content
|
||||||
|
* @param {string} fileName
|
||||||
|
* @param {string} outputDirectory
|
||||||
|
*/
|
||||||
|
export const appendFile = (content, fileName, outputDirectory) =>
|
||||||
|
fs.appendFileSync(path.join(outputDirectory, fileName), content, 'utf-8');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* writes content to a file
|
||||||
|
*
|
||||||
|
* @param {string} content
|
||||||
|
* @param {string} fileName
|
||||||
|
* @param {string} outputDirectory
|
||||||
|
*/
|
||||||
|
export const writeFile = (content, fileName, outputDirectory) =>
|
||||||
|
fs.writeFileSync(path.join(outputDirectory, fileName), content, 'utf-8');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* reads the icon directory
|
||||||
|
*
|
||||||
|
* @param {string} directory
|
||||||
|
*/
|
||||||
|
export const readSvgDirectory = directory =>
|
||||||
|
fs.readdirSync(directory).filter(file => path.extname(file) === '.svg');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read svg from directory
|
||||||
|
*
|
||||||
|
* @param {string} fileName
|
||||||
|
* @param {string} directory
|
||||||
|
*/
|
||||||
|
export const readSvg = (fileName, directory) => fs.readFileSync(path.join(directory, fileName));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* writes content to a file
|
||||||
|
*
|
||||||
|
* @param {string} fileName
|
||||||
|
* @param {string} outputDirectory
|
||||||
|
* @param {string} content
|
||||||
|
*/
|
||||||
|
export const writeSvgFile = (fileName, outputDirectory, content) =>
|
||||||
|
fs.appendFileSync(path.join(outputDirectory, fileName), content, 'utf-8');
|
||||||
15
scripts/optimizeSvgs.js
Normal file
15
scripts/optimizeSvgs.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import processSvg from './render/processSvg';
|
||||||
|
import { readSvgDirectory, writeSvgFile } from './helpers';
|
||||||
|
|
||||||
|
const ICONS_DIR = path.resolve(__dirname, '../icons');
|
||||||
|
|
||||||
|
console.log(`Optimizing SVGs...`);
|
||||||
|
|
||||||
|
const svgFiles = readSvgDirectory(ICONS_DIR);
|
||||||
|
|
||||||
|
svgFiles.forEach(svgFile => {
|
||||||
|
const content = fs.readFileSync(path.join(ICONS_DIR, svgFile));
|
||||||
|
processSvg(content).then(svg => writeSvgFile(svg, ICONS_DIR, content));
|
||||||
|
});
|
||||||
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;
|
||||||
|
};
|
||||||
35
scripts/render/renderIconsObject.js
Normal file
35
scripts/render/renderIconsObject.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/* eslint-disable import/no-extraneous-dependencies */
|
||||||
|
import path from 'path';
|
||||||
|
import cheerio from 'cheerio';
|
||||||
|
import { minify } from 'html-minifier';
|
||||||
|
import { readSvg } from '../helpers';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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, iconsDirectory) =>
|
||||||
|
svgFiles
|
||||||
|
.map(svgFile => {
|
||||||
|
const name = path.basename(svgFile, '.svg');
|
||||||
|
const svg = readSvg(svgFile, iconsDirectory);
|
||||||
|
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.createElementNS('http://www.w3.org/2000/svg', 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
|
||||||
|
*/
|
||||||
34
src/lucide.js
Normal file
34
src/lucide.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import replaceElement from './replaceElement';
|
||||||
|
import * as allIcons from './icons/index';
|
||||||
|
|
||||||
|
/*
|
||||||
|
Create icons
|
||||||
|
*/
|
||||||
|
export const createIcons = ({ icons = {}, nameAttr = 'icon-name', attrs = {} } = {}) => {
|
||||||
|
if (!Object.values(icons).length) {
|
||||||
|
throw new Error(
|
||||||
|
"Please provide an icons object.\nIf you want to use all the icons you can import it like:\n `import { createIcons, icons } from 'lucide';\nlucide.createIcons({icons});`",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof document === 'undefined') {
|
||||||
|
throw new Error('`createIcons()` only works in a browser environment.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const elementsToReplace = document.querySelectorAll(`[${nameAttr}]`);
|
||||||
|
|
||||||
|
Array.from(elementsToReplace).forEach(element =>
|
||||||
|
replaceElement(element, { nameAttr, icons, attrs }),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Create Element function export.
|
||||||
|
*/
|
||||||
|
export { default as createElement } from './createElement';
|
||||||
|
|
||||||
|
/*
|
||||||
|
Icons exports.
|
||||||
|
*/
|
||||||
|
export { allIcons as icons };
|
||||||
|
export * from './icons/index';
|
||||||
83
src/replaceElement.js
Normal file
83
src/replaceElement.js
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import { camelCase, upperFirst } from 'lodash-es';
|
||||||
|
import createElement from './createElement';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the attributes of an HTML element.
|
||||||
|
* @param {HTMLElement} element
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
export const getAttrs = element =>
|
||||||
|
Array.from(element.attributes).reduce((attrs, attr) => {
|
||||||
|
attrs[attr.name] = attr.value;
|
||||||
|
return attrs;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the classNames of an attributes Object.
|
||||||
|
* @param {Object} attrs
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
export const getClassNames = attrs => {
|
||||||
|
if (!attrs || !attrs.class) return '';
|
||||||
|
if (attrs.class && typeof attrs.class === 'string') {
|
||||||
|
return attrs.class.split(' ');
|
||||||
|
}
|
||||||
|
if (attrs.class && Array.isArray(attrs.class)) {
|
||||||
|
return attrs.class;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Combines the classNames of array of classNames to a String
|
||||||
|
* @param {Array} arrayOfClassnames
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
export const combineClassNames = arrayOfClassnames => {
|
||||||
|
const classNameArray = arrayOfClassnames.flatMap(getClassNames);
|
||||||
|
|
||||||
|
return classNameArray
|
||||||
|
.map(classItem => classItem.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ReplaceElement, replaces the given element with the created icon.
|
||||||
|
* @param {HTMLElement} element
|
||||||
|
* @param {Object: {String, Array, Object}} options: { nameAttr, icons, attrs }
|
||||||
|
* @returns {Function}
|
||||||
|
*/
|
||||||
|
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 elementAttrs = getAttrs(element);
|
||||||
|
|
||||||
|
const [, iconAttrs] = iconNode;
|
||||||
|
|
||||||
|
const allAttrs = {
|
||||||
|
...iconAttrs,
|
||||||
|
...attrs,
|
||||||
|
};
|
||||||
|
|
||||||
|
iconNode[1] = { ...allAttrs };
|
||||||
|
|
||||||
|
const classNames = combineClassNames([iconAttrs, elementAttrs, attrs]);
|
||||||
|
|
||||||
|
if (classNames) {
|
||||||
|
iconNode[1].class = classNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
const svgElement = createElement(iconNode);
|
||||||
|
|
||||||
|
return element.parentNode.replaceChild(svgElement, element);
|
||||||
|
};
|
||||||
3
tests/__snapshots__/lucide.spec.js.snap
Normal file
3
tests/__snapshots__/lucide.spec.js.snap
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`createIcons should read elements from DOM and replace it with icons 1`] = `"<svg 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\\"><polygon points=\\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\\"></polygon><path d=\\"M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07\\"></path></svg>"`;
|
||||||
3
tests/__snapshots__/replaceElement.spec.js.snap
Normal file
3
tests/__snapshots__/replaceElement.spec.js.snap
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`combineClassNames should retuns a string of classNames 1`] = `"item1 item2 item3 item4 item5 item6 item7 item8 item9"`;
|
||||||
21
tests/icons/download.js
Normal file
21
tests/icons/download.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
const Download = [
|
||||||
|
"svg",
|
||||||
|
{
|
||||||
|
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"
|
||||||
|
},
|
||||||
|
[
|
||||||
|
["path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }],
|
||||||
|
["polyline", { points: "7 10 12 15 17 10" }],
|
||||||
|
["line", { x1: "12", y1: "15", x2: "12", y2: "3" }]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
export default Download;
|
||||||
27
tests/icons/globe.js
Normal file
27
tests/icons/globe.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
const Globe = [
|
||||||
|
"svg",
|
||||||
|
{
|
||||||
|
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"
|
||||||
|
},
|
||||||
|
[
|
||||||
|
["circle", { cx: "12", cy: "12", r: "10" }],
|
||||||
|
["line", { x1: "2", y1: "12", x2: "22", y2: "12" }],
|
||||||
|
[
|
||||||
|
"path",
|
||||||
|
{
|
||||||
|
d:
|
||||||
|
"M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
export default Globe;
|
||||||
6
tests/icons/index.js
Normal file
6
tests/icons/index.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
export { default as Download } from './download';
|
||||||
|
export { default as Globe } from './globe';
|
||||||
|
export { default as Menu } from './menu';
|
||||||
|
export { default as Moon } from './moon';
|
||||||
|
export { default as Volume2 } from './volume-2';
|
||||||
21
tests/icons/menu.js
Normal file
21
tests/icons/menu.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
const Menu = [
|
||||||
|
"svg",
|
||||||
|
{
|
||||||
|
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"
|
||||||
|
},
|
||||||
|
[
|
||||||
|
["line", { x1: "3", y1: "12", x2: "21", y2: "12" }],
|
||||||
|
["line", { x1: "3", y1: "6", x2: "21", y2: "6" }],
|
||||||
|
["line", { x1: "3", y1: "18", x2: "21", y2: "18" }]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
export default Menu;
|
||||||
17
tests/icons/moon.js
Normal file
17
tests/icons/moon.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
const Moon = [
|
||||||
|
"svg",
|
||||||
|
{
|
||||||
|
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"
|
||||||
|
},
|
||||||
|
[["path", { d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" }]]
|
||||||
|
];
|
||||||
|
|
||||||
|
export default Moon;
|
||||||
23
tests/icons/volume-2.js
Normal file
23
tests/icons/volume-2.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
const Volume2 = [
|
||||||
|
"svg",
|
||||||
|
{
|
||||||
|
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"
|
||||||
|
},
|
||||||
|
[
|
||||||
|
["polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }],
|
||||||
|
[
|
||||||
|
"path",
|
||||||
|
{ d: "M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07" }
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
export default Volume2;
|
||||||
59
tests/lucide.spec.js
Normal file
59
tests/lucide.spec.js
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import * as icons from './icons';
|
||||||
|
import { createIcons } from '../src/lucide';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { minify } from 'html-minifier';
|
||||||
|
|
||||||
|
const ICONS_DIR = path.resolve(__dirname, '../icons');
|
||||||
|
|
||||||
|
const getOriginalSvg = (iconName) => {
|
||||||
|
const svgContent = fs.readFileSync(path.join(ICONS_DIR, `${iconName}.svg`), 'utf8');
|
||||||
|
|
||||||
|
return minify(svgContent, { collapseWhitespace: true, keepClosingSlash: true, });
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('createIcons', () => {
|
||||||
|
it('should read elements from DOM and replace it with icons', () => {
|
||||||
|
document.body.innerHTML = `<i icon-name="volume-2"></i>`;
|
||||||
|
|
||||||
|
createIcons({icons});
|
||||||
|
|
||||||
|
const svg = getOriginalSvg('volume-2');
|
||||||
|
expect(document.body.innerHTML).toMatchSnapshot()
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should customize the name attribute', () => {
|
||||||
|
document.body.innerHTML = `<i custom-name="volume-2"></i>`;
|
||||||
|
|
||||||
|
createIcons({
|
||||||
|
icons,
|
||||||
|
nameAttr: 'custom-name'
|
||||||
|
});
|
||||||
|
|
||||||
|
const hasSvg = !!document.querySelector('svg');
|
||||||
|
|
||||||
|
expect(hasSvg).toBeTruthy()
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add custom attributes', () => {
|
||||||
|
document.body.innerHTML = `<i icon-name="volume-2"></i>`;
|
||||||
|
|
||||||
|
const attrs = {
|
||||||
|
class: 'icon custom-class',
|
||||||
|
fill: 'black',
|
||||||
|
};
|
||||||
|
|
||||||
|
createIcons({ icons, attrs });
|
||||||
|
|
||||||
|
const element = document.querySelector('svg');
|
||||||
|
const attributes = element.getAttributeNames();
|
||||||
|
|
||||||
|
const attributesAndValues = attributes.reduce((acc, item) => {
|
||||||
|
acc[item] = element.getAttribute(item);
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
},{})
|
||||||
|
|
||||||
|
expect(attributesAndValues).toEqual(expect.objectContaining(attrs));
|
||||||
|
});
|
||||||
|
});
|
||||||
62
tests/replaceElement.spec.js
Normal file
62
tests/replaceElement.spec.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import { getAttrs, getClassNames, combineClassNames } from '../src/replaceElement';
|
||||||
|
|
||||||
|
describe('getAtts', () => {
|
||||||
|
it('should returns attrbrutes of an element', () => {
|
||||||
|
const element = {
|
||||||
|
attributes: [
|
||||||
|
{
|
||||||
|
name: 'class',
|
||||||
|
value: 'item1 item2 item4',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'date-name',
|
||||||
|
value: 'volume',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const attrs = getAttrs(element);
|
||||||
|
|
||||||
|
expect(attrs.class).toBe(element.attributes[0].value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getClassNames', () => {
|
||||||
|
it('should returns an array when giving class property of string', () => {
|
||||||
|
const elementAttrs = {
|
||||||
|
class: 'item1 item2 item3'
|
||||||
|
};
|
||||||
|
|
||||||
|
const attrs = getClassNames(elementAttrs);
|
||||||
|
expect(JSON.stringify(attrs)).toBe(JSON.stringify(['item1','item2','item3']));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should returns an array when givind class property with an array', () => {
|
||||||
|
const elementAttrs = {
|
||||||
|
class: ['item1','item2','item3']
|
||||||
|
};
|
||||||
|
|
||||||
|
const attrs = getClassNames(elementAttrs);
|
||||||
|
expect(JSON.stringify(attrs)).toBe(JSON.stringify(['item1','item2','item3']));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('combineClassNames', () => {
|
||||||
|
it('should retuns a string of classNames', () => {
|
||||||
|
const arrayOfClassnames = [
|
||||||
|
{
|
||||||
|
class: ['item1','item2','item3']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
class: ['item4','item5','item6']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
class: ['item7','item8','item9']
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const combinedClassNames = combineClassNames(arrayOfClassnames);
|
||||||
|
|
||||||
|
expect(combinedClassNames).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user