Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(spark-icons): add icon file builder scripts #133

Merged
merged 10 commits into from
Jul 14, 2021
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/libs/spark/src/icons/*
/libs/spark-icons/defaults/templateSvgIcon.tsx
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,6 @@ testem.log
# System Files
.DS_Store
Thumbs.db

# @prenda/spark-icons Raw Downloads
/libs/spark-icons/svg-files
3 changes: 1 addition & 2 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# Add files here to ignore them from prettier formatting

/dist
/coverage
/libs/spark/src/icons/*
/libs/spark-icons/defaults/templateSvgIcon.tsx
46 changes: 46 additions & 0 deletions libs/spark-icons/BUILDER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
Original Credit to https://github.com/mui-org/material-ui/blob/1c5beec4be20eae30e75c69ab513bbfec3e9baaf/packages/material-ui-icons/README.md

# @prenda/spark-icons builder (Derived from @material-ui/icons-builder)

This tool generates Material-UI SvgIcon components for a set of svg icons.

## Running the builder

The builder script generates the Spark Design System Icons.

```sh
# in project root
npm install
npm run src:icons
```

## Process

Prerequisite: you must _manually_ bulk-export SVG icons from Figma.

- Use the [SVG Export](https://www.figma.com/community/plugin/814345141907543603/SVG-Export), with a preset that applies only these optimizations (these two help avoid there being a `<g fillRule="evenodd">...</g>` parent tag to two paths in some Duotone icons, like Rocket):
- Move group attrs to elements
- Collapse useless groups
- Save each `.zip` with the name as their respective page name (`line`, `filled`, `duotone`).
- Unzip and move folders under `libs/spark-icons/svg-files`.

```bash
unzip line.zip && cp -r <source_path>/line/* <dest_path>/libs/spark-icons/svg-files/line
unzip filled.zip && cp -r <source_path>/line/* <dest_path>/libs/spark-icons/svg-files/filled
unzip duotone.zip && cp -r <source_path>/line/* <dest_path>/libs/spark-icons/svg-files/duotone
```

The build script generates the appropriate `.js` files in the `src/` folder.

### Planned

The builder script downloads Spark Design System SVG icons to the `svg-files` folder without any manual intervention.

## Advanced usage / Custom builds

`builder.js` has options available for building:

- `--output-dir` - Directory to output generated components.
- `--svg-dir` - Directory containing the source SVG icons.
- `--file-suffix` - Process only files ending with the specified suffix.
- `--rename-filter` - Apply a custom filter to rename the generated icons.
36 changes: 36 additions & 0 deletions libs/spark-icons/defaults/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Original credit to https://github.com/mui-org/material-ui/blob/1c5beec4be20eae30e75c69ab513bbfec3e9baaf/babel.config.js
// Changes made since

let defaultPresets;

if (process.env.BABEL_ENV === 'es') {
defaultPresets = [];
} else {
defaultPresets = [
[
'@babel/preset-env',
{
bugfixes: true,
modules: ['esm', 'production-umd'].includes(process.env.BABEL_ENV)
? false
: 'commonjs',
},
],
];
}

module.exports = {
presets: defaultPresets,
plugins: [['@babel/plugin-proposal-class-properties', { loose: true }]],
env: {
cjs: {
plugins: [],
},
es: {
plugins: [['@babel/plugin-transform-runtime', { useESModules: true }]],
},
esm: {
plugins: [['@babel/plugin-transform-runtime', { useESModules: true }]],
},
},
};
36 changes: 36 additions & 0 deletions libs/spark-icons/defaults/renameFilter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Original credit to https://github.com/mui-org/material-ui/blob/1c5beec4be20eae30e75c69ab513bbfec3e9baaf/packages/material-ui-icons/renameFilters/material-design-icons.js
// Changes made since

const SUFFIXES = {
line: '',
filled: 'Filled',
duotone: 'Duotone',
};
const KNOWN_SUFFIXES = Object.keys(SUFFIXES);

function myDestRewriter(svgPathObj) {
let fileName = svgPathObj.base;

fileName = fileName
.replace(/.svg/, '.tsx')
.replace(
/(^.)|(\s*_\s*)(.)|(\s*-\s*)(.)|(\s+)(.)/g,
(match, p1, p2, p3, p4, p5, p6, p7) =>
(p1 || p3 || p5 || p7).toUpperCase()
);

if (fileName.indexOf('360') === 0) {
fileName = `ThreeSixty${fileName.slice(3)}`;
}

const maybeSuffix = svgPathObj.dir.split('/').slice(-1)[0];
const suffix = KNOWN_SUFFIXES.includes(maybeSuffix)
? SUFFIXES[maybeSuffix]
: '';

fileName = fileName.split('.tsx')[0].concat(suffix).concat('.tsx');

return fileName;
}

export default myDestRewriter;
6 changes: 6 additions & 0 deletions libs/spark-icons/defaults/templateSvgIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import * as React from 'react';
import createSvgIcon from './utils/createSvgIcon';

export default createSvgIcon(
{{{paths}}}
, '{{componentName}}');
62 changes: 62 additions & 0 deletions libs/spark-icons/modules/waterfall/Queue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Original credit to https://github.com/mui-org/material-ui/blob/1c5beec4be20eae30e75c69ab513bbfec3e9baaf/modules/waterfall/Queue.js
import waitUntil from './waitUntil';

class Queue {
pendingEntries = [];

inFlight = 0;

err = null;

constructor(worker, options = {}) {
this.worker = worker;
this.concurrency = options.concurrency || 1;
}

push = (entries) => {
this.pendingEntries = this.pendingEntries.concat(entries);
this.process();
};

process = () => {
const scheduled = this.pendingEntries.splice(
0,
this.concurrency - this.inFlight
);
this.inFlight += scheduled.length;
scheduled.forEach(async (task) => {
try {
await this.worker(task);
} catch (err) {
this.err = err;
} finally {
this.inFlight -= 1;
}

if (this.pendingEntries.length > 0) {
this.process();
}
});
};

wait = (options = {}) =>
waitUntil(
() => {
if (this.err) {
this.pendingEntries = [];
throw this.err;
}

return {
predicate: options.empty
? this.inFlight === 0 && this.pendingEntries.length === 0
: this.concurrency > this.pendingEntries.length,
};
},
{
delay: 50,
}
);
}

export default Queue;
8 changes: 8 additions & 0 deletions libs/spark-icons/modules/waterfall/sleep.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Full credit to https://github.com/mui-org/material-ui/blob/1c5beec4be20eae30e75c69ab513bbfec3e9baaf/modules/waterfall/sleep.js
function sleep(delay = 0) {
return new Promise((resolve) => {
setTimeout(resolve, delay);
});
}

export default sleep;
18 changes: 18 additions & 0 deletions libs/spark-icons/modules/waterfall/waitUntil.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Original credit to https://github.com/mui-org/material-ui/blob/1c5beec4be20eae30e75c69ab513bbfec3e9baaf/modules/waterfall/waitUntil.js
import sleep from './sleep';

export default async function waitUntil(test, options = {}) {
const { delay = 5e3, tries = -1 } = options;
const { predicate, result } = await test();

if (predicate) {
return result;
}

if (tries - 1 === 0) {
throw new Error('tries limit reached');
}

await sleep(delay);
return waitUntil(test, { ...options, tries: tries > 0 ? tries - 1 : tries });
}
3 changes: 2 additions & 1 deletion libs/spark-icons/package.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"name": "@prenda/spark-icons",
"version": "0.0.1"
"version": "0.0.1",
"sideEffects": false
}
Loading