diff --git a/Makefile b/Makefile
index 4b4092b..7a28afc 100644
--- a/Makefile
+++ b/Makefile
@@ -1,13 +1,16 @@
src_files := src/*.js
src_dir := src
-.PHONY: all lint build
+.PHONY: all lint test build
-all: lint build
+all: lint test build
lint: dist/icons.json
./node_modules/.bin/eslint .
+test:
+ ./node_modules/.bin/jest
+
build: dist/feather.js dist/feather.min.js dist/icons
node_modules:
diff --git a/bin/__tests__/__snapshots__/process-svg.test.js.snap b/bin/__tests__/__snapshots__/process-svg.test.js.snap
new file mode 100644
index 0000000..75c8ebe
--- /dev/null
+++ b/bin/__tests__/__snapshots__/process-svg.test.js.snap
@@ -0,0 +1,26 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`processes SVG correctly 1`] = `
+"
+"
+`;
+
+exports[`rejects when passed unparsable SVG string 1`] = `
+[Error: Error in parsing SVG: Unclosed root tag
+Line: 0
+Column: 10
+Char: ]
+`;
diff --git a/bin/__tests__/process-svg.test.js b/bin/__tests__/process-svg.test.js
new file mode 100644
index 0000000..79c63ff
--- /dev/null
+++ b/bin/__tests__/process-svg.test.js
@@ -0,0 +1,15 @@
+/* eslint-env jest */
+import processSvg from '../process-svg';
+
+test('processes SVG correctly', () => {
+ const SVG =
+ '';
+
+ expect(processSvg(SVG)).resolves.toMatchSnapshot();
+});
+
+test('rejects when passed unparsable SVG string', () => {
+ const UNPARSABLE_SVG = '}
+ */
+function processSvg(svg) {
+ return (
+ optimize(svg)
+ .then(setAttributes)
+ .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}
+ */
+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 setAttributes(svg) {
+ const $ = cheerio.load(svg);
+
+ Object.keys(DEFAULT_ATTRIBUTES).forEach(key =>
+ $('svg').attr(key, DEFAULT_ATTRIBUTES[key]),
+ );
+
+ return $('body').html();
+}
+
+export default processSvg;
diff --git a/bin/process-svgs.js b/bin/process-svgs.js
new file mode 100644
index 0000000..3bd4981
--- /dev/null
+++ b/bin/process-svgs.js
@@ -0,0 +1,15 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import fs from 'fs';
+import path from 'path';
+
+import processSvg from './process-svg';
+
+const ICONS_DIR = path.resolve(__dirname, '../icons');
+
+fs
+ .readdirSync(ICONS_DIR)
+ .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));
+ });
diff --git a/icons/activity.svg b/icons/activity.svg
index 3584132..2eccd9c 100644
--- a/icons/activity.svg
+++ b/icons/activity.svg
@@ -1,3 +1,13 @@
-