Skip to content

Commit

Permalink
feat: icon subset (#407)
Browse files Browse the repository at this point in the history
* feat: enabled icon subset completely

* chore: updated api

* chore: updated from main

* fix: issue with tests in cicd by installing fonttools

* Update IconSubset.md

* Update .github/workflows/01-test.yml

Co-authored-by: Maximilian Franzke <[email protected]>

* Update docs/IconSubset.md

Co-authored-by: Maximilian Franzke <[email protected]>

* Update docs/IconSubset.md

Co-authored-by: Maximilian Franzke <[email protected]>

* Update src/commands/icon-subset/data.ts

Co-authored-by: Maximilian Franzke <[email protected]>

* Update src/commands/icon-subset/helpers/filter-icons.ts

Co-authored-by: Maximilian Franzke <[email protected]>

* Update src/commands/icon-subset/helpers/ttx.ts

Co-authored-by: Maximilian Franzke <[email protected]>

* Update src/commands/icon-subset/helpers/ttx.ts

Co-authored-by: Maximilian Franzke <[email protected]>

* Update src/commands/icon-subset/helpers/ttx.ts

Co-authored-by: Maximilian Franzke <[email protected]>

* chore: update from main

* refactor: replaced Src by Source

* Update src/commands/icon-subset/helpers/filter-icons.ts

Co-authored-by: Maximilian Franzke <[email protected]>

* Update docs/IconSubset.md

Co-authored-by: Maximilian Franzke <[email protected]>

* Update src/commands/icon-subset/helpers/filter-icons.ts

Co-authored-by: Maximilian Franzke <[email protected]>

* Update src/commands/icon-subset/helpers/filter-icons.ts

Co-authored-by: Maximilian Franzke <[email protected]>

* Update src/utils/shared.ts

Co-authored-by: Maximilian Franzke <[email protected]>

* Update docs/API.md

Co-authored-by: Maximilian Franzke <[email protected]>

* Update test/icon-subset/simple/simple.test.ts

Co-authored-by: Maximilian Franzke <[email protected]>

* Update ttx.ts

* Update test/icon-subset/config/config.test.ts

Co-authored-by: Maximilian Franzke <[email protected]>

* Update src/commands/icon-subset/index.ts

Co-authored-by: Maximilian Franzke <[email protected]>

---------

Co-authored-by: Maximilian Franzke <[email protected]>
  • Loading branch information
nmerget and mfranzke authored Jan 14, 2025
1 parent b8eb653 commit c4fbaf5
Show file tree
Hide file tree
Showing 23 changed files with 611 additions and 44 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/01-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ jobs:
- name: ⏬ Checkout repo
uses: actions/checkout@v4

- name: ⏬ Install fonttools
run: |
pip3 install fonttools brotli
fonttools --help
- name: 🔄 Init PNPM
uses: ./.github/actions/pnpm-init

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@ yarn.lock
/fonts/
/test/**/fonts/
**/generated/
**/*.tmp.*
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ By default, Cosmiconfig will check the current directory for the following:

- [Clean icons](./docs/CleanIcons.md)
- [Generate Icon Fonts](./docs/GenerateIconFonts.md)
- [Icon subset](./docs/IconSubset.md)
75 changes: 47 additions & 28 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,55 @@ CLI for generating or manipulating icon fonts from SVG files.

Clean svgs for icon fonts to work

> You can use `clean-icons.json` as a config file.
By default it tries to search for the configuration otherwise use a correct path by passing `--config=./clean-icons.json`.

| long | short | description | required | defaultValue |
| :------------------ | :------: | :-------------------------------------------------------------------------------------------------- | :------: | :----------- |
| `--src` | `-s` | Src folder with all svgs | `` | |
| `--traceResolution` | `-t` | Change the default resolution of the trace | `` | `"600"` |
| `--out` | `-o` | Relative path where the files should be written to. Empty string will overwrite the original files. | `` | |
| `--dry` | `-d` | Do a dry run with this command - prints/returns output | `` | |
| `--ignoreGlobs` | `-ig` | Path icon glob to exclude from the fonts | `` | `[]` |
| `--debug` | `-debug` | Extra logging | `` | |
| `--config` | `-c` | Path to configuration file | `` | |
> You can use `clean-icons.json` as a config file.
> By default it tries to search for the configuration otherwise use a correct path by passing `--config=./clean-icons.json`.
| long | short | description | required | defaultValue |
| :------------------ | :---: | :-------------------------------------------------------------------------------------------------- | :------: | :----------- |
| `--src` | `-s` | Source folder with all svgs | `` | |
| `--traceResolution` | `-r` | Change the default resolution of the trace | `` | `"600"` |
| `--out` | `-o` | Relative path where the files should be written to. Empty string will overwrite the original files. | `` | |
| `--dry` | `-d` | Do a dry run with this command - prints/returns output | `` | |
| `--ignoreGlobs` | `-i` | Path icon glob to exclude from the fonts | `` | `[]` |
| `--debug` | | Extra logging | `` | |
| `--config` | `-c` | Path to configuration file | `` | |

## generate-icon-fonts

Generate icon fonts from SVG files

> You can use `generate-icon-fonts.json` as a config file.
By default it tries to search for the configuration otherwise use a correct path by passing `--config=./generate-icon-fonts.json`.

| long | short | description | required | defaultValue |
| :------------------- | :------: | :------------------------------------------------------------------------------------ | :------: | :----------- |
| `--fontName` | `-fn` | The name of your font | `` | |
| `--src` | `-s` | Src folder with all svgs | `` | |
| `--variants` | `-var` | Font variants e.g. solid, inverted, etc. We always add a "default" variant for icons. | `` | `[]` |
| `--withSizes` | `-ws` | Splits the font into different sizes | `` | |
| `--prefix` | `-p` | Prefix of icons to delete for icons | `` | |
| `--overwriteSources` | `-ows` | Overwrite all svgs inside src directory | `` | |
| `--dry` | `-d` | Do a dry run with this command - prints/returns output | `` | |
| `--ignoreGlobs` | `-ig` | Path icon glob to exclude from the fonts | `` | `[]` |
| `--debug` | `-debug` | Extra logging | `` | |
| `--config` | `-c` | Path to configuration file | `` | |

> You can use `generate-icon-fonts.json` as a config file.
> By default it tries to search for the configuration otherwise use a correct path by passing `--config=./generate-icon-fonts.json`.
| long | short | description | required | defaultValue |
| :------------------- | :---: | :------------------------------------------------------------------------------------ | :------: | :----------- |
| `--fontName` | `-f` | The name of your font | `` | |
| `--src` | `-s` | Source folder with all svgs | `` | |
| `--variants` | `-v` | Font variants e.g. solid, inverted, etc. We always add a "default" variant for icons. | `` | `[]` |
| `--withSizes` | `-w` | Splits the font into different sizes | `` | |
| `--prefix` | `-p` | Prefix of icons to delete for icons | `` | |
| `--overwriteSources` | `-o` | Overwrite all svgs inside src directory | `` | |
| `--dry` | `-d` | Do a dry run with this command - prints/returns output | `` | |
| `--ignoreGlobs` | `-i` | Path icon glob to exclude from the fonts | `` | `[]` |
| `--debug` | | Extra logging | `` | |
| `--config` | `-c` | Path to configuration file | `` | |

## icon-subset

Creates new icon font (woff2) by passing a safelist/blocklist with icon literals. Requires <https://github.com/fonttools/fonttools> to be installed.

> You can use `icon-subset.json` as a config file.
> By default it tries to search for the configuration otherwise use a correct path by passing `--config=./icon-subset.json`.
| long | short | description | required | defaultValue |
| :-------------- | :---: | :----------------------------------------------------------------------------- | :------: | :----------- |
| `--safeList` | `-l` | Includes only icons from provided list. Priority over 'blockList'. | `` | |
| `--blockList` | `-b` | Excludes all icons from provided list. Can't be used together with 'safeList'. | `` | |
| `--overwrite` | `-w` | If the source file should be overwritten. Disables 'out' option. | `` | `true` |
| `--src` | `-s` | Source folder with all woff2 files | `` | |
| `--filePaths` | `-f` | File paths to woff2 file which should be subsetted. | `` | |
| `--out` | `-o` | Relative path where the file should be written | `` | `"."` |
| `--dry` | `-d` | Do a dry run with this command - prints/returns output | `` | |
| `--debug` | | Extra logging | `` | |
| `--config` | `-c` | Path to configuration file | `` | |
| `--ignoreGlobs` | `-i` | Path icon glob to exclude from the fonts | `` | `[]` |
99 changes: 99 additions & 0 deletions docs/IconSubset.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Icon subset

Remove unused icons from your icon font (`woff2`) to reduce file size.

> **Note:** Requires https://github.com/fonttools/fonttools to be installed. Most of the time you need it in production. In this case look at [CI/CD section](#cicd).
## Requirements - local

To subset icon fonts we use [fonttools](https://github.com/fonttools/fonttools). To use the tools you need python installed:

1. Install [python](https://docs.python-guide.org/starting/installation/#installation) and [ensure that you can run Python from the command line](https://packaging.python.org/en/latest/tutorials/installing-packages/#ensure-you-can-run-python-from-the-command-line)
2. Install fonttools: `pip3 install fonttools`
3. Install brotli: `pip3 install brotli`

## Use

### Config

Create a new file `icon-subset.json` with this content:

```json
{
"src": "./my-path-to/icons",
"safeList": ["airplane", "bell"]
}
```

### JS/TS

Here is an example for a JS file `index.(js|ts)`:

```js
// index.(js|ts)
import { iconSubset } from "@db-ux/icon-font-tools/dist/commands/icon-subset";

void iconSubset({
src: "./my-path-to/icons",
safeList: ["airplane", "bell"],
});
```

### CLI

Example:

```shell
npx @db-ux/icon-font-tools icon-subset --src ./my-path-to/icons --safeList airplane --safeList bell
```

For more information run:

```shell
npx @db-ux/icon-font-tools icon-subset --help
```

## CI/CD

In most cases you want some `build` job which produces some `dist` folder. We can optimize this `dist` folder before any e2e tests or deployment like this:

### GitHub Actions

The default GitHub image is already bundled with `nodejs` and `python`. You can use the following snippet to install the required tools:

```yaml
jobs:
icon-subset:
name: 🤓🔪 Icon subset
runs-on: ubuntu-latest
steps:
- name: ⏬ Checkout repo
uses: actions/checkout@v4

- name: ⏬ Install fonttools
run: |
pip3 install fonttools brotli
fonttools --help
- name: 🏃🏃‍➡️ Run Icon subset
run: npx @db-ux/icon-font-tools icon-subset
```
### GitLab CI
```yaml
stages:
- post_build

post_build:
stage: post_build
image: nikolaik/python-nodejs:latest
before_script:
- pip3 install fonttools brotli
- fonttools --help
script:
- npx @db-ux/icon-font-tools icon-subset
artifacts:
paths:
- dist # Your dist/build folder
```
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,14 @@
"rimraf": "^6.0.1",
"svgicons2svgfont": "15.0.0",
"svgo": "^3.3.2",
"svgtofont": "^6.0.0"
"svgtofont": "^6.0.0",
"xml2js": "^0.6.2"
},
"devDependencies": {
"@eslint/js": "^9.11.1",
"@types/fs-extra": "^11.0.4",
"@types/node": "^22.7.3",
"@types/xml2js": "^0.4.14",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"cpr": "3.0.1",
Expand Down
22 changes: 22 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/api-docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const generateApiDocs = (name: string, programDescription: string) => {
.sort((a, b) => (a.required === b.required ? 0 : a.required ? -1 : 1))) {
mTable.push([
`\`${long}\``,
`\`${short}\``,
short && short?.length > 0 ? `\`${short}\`` : "",
description,
`\`${getBooleanValue(required)}\``,
defaultValue ? `\`${JSON.stringify(defaultValue)}\`` : "",
Expand Down
2 changes: 2 additions & 0 deletions src/commands/clean-icons/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ export type CleanIconsConfigType = {

export const cleanIconsOptions: ProgramOptionsType[] = [
{
short: "r",
name: "traceResolution",
description: "Change the default resolution of the trace",
defaultValue: "600",
},
{
short: "o",
name: "out",
description:
"Relative path where the files should be written to. Empty string will overwrite the original files.",
Expand Down
8 changes: 4 additions & 4 deletions src/commands/generate-icon-fonts/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@ export type GifConfigType = {

export const gifOptions: ProgramOptionsType[] = [
{
short: "var",
short: "v",
name: "variants",
description:
'Font variants e.g. solid, inverted, etc. We always add a "default" variant for icons.',
array: true,
defaultValue: [],
},
{
short: "ws",
short: "w",
name: "withSizes",
description: "Splits the font into different sizes",
required: false,
Expand All @@ -60,7 +60,7 @@ export const gifOptions: ProgramOptionsType[] = [
description: "Prefix of icons to delete for icons",
},
{
short: "fn",
short: "f",
name: "fontName",
description: "The name of your font",
required: true,
Expand All @@ -70,7 +70,7 @@ export const gifOptions: ProgramOptionsType[] = [
},
{
name: "overwriteSources",
short: "ows",
short: "o",
description: "Overwrite all svgs inside src directory",
defaultValue: false,
},
Expand Down
16 changes: 16 additions & 0 deletions src/commands/icon-subset/action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { IconSubsetConfigType, iconSubsetOptions } from "./data";
import { startConfigProcess } from "../../utils/config-process";
import { ICON_SUBSET_COMMAND } from "../../data";
import { startInquirerProcess } from "../../utils/inquirer-process";
import { iconSubset } from "./index";

export const iconSubsetAction = async (passedConfig: IconSubsetConfigType) => {
let config = await startConfigProcess(ICON_SUBSET_COMMAND, passedConfig);

config = await startInquirerProcess<IconSubsetConfigType>(
config,
iconSubsetOptions,
);

return await iconSubset(config);
};
Loading

0 comments on commit c4fbaf5

Please sign in to comment.