Skip to content

Commit 87f8157

Browse files
authored
feat: Custom getIconID option
- Configurable `getIconId` option
2 parents 2793c78 + 4f128b0 commit 87f8157

File tree

18 files changed

+258
-55
lines changed

18 files changed

+258
-55
lines changed

README.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ fantasticon my-icons -o icon-dist
3535

3636
### Command-line
3737

38-
**Note:** Not all options can be specified through the command line - for `formatOptions`, `pathOptions` and `templates` use a [configuration file](#configuration-file) or the JavaScript API.
38+
**Note:** Not all options can be specified through the command line - for `formatOptions`, `pathOptions`, `getIconId` and `templates` use a [configuration file](#configuration-file) or the JavaScript [API](#api).
3939

4040
```
4141
Usage: fantasticon [options] [input-dir]
@@ -62,7 +62,7 @@ Options:
6262

6363
### Configuration file
6464

65-
Some options (specifically, `formatOptions` and `pathOptions`) cannot be passed to the CLI directly.
65+
Some options (specifically, `formatOptions`, `pathOptions` and `getIconId`) cannot be passed to the CLI directly.
6666

6767
To have more control and better readability, you can create a simple configuration file.
6868

@@ -113,7 +113,15 @@ module.exports = {
113113
'chevron-right': 57345,
114114
'thumbs-up': 57358,
115115
'thumbs-down': 57359
116-
}
116+
},
117+
// Customize generated icon IDs (unavailable with `.json` config file)
118+
getIconId: ({
119+
basename, // `string` - Example: 'foo';
120+
relativeDirPath, // `string` - Example: 'sub/dir/foo.svg'
121+
absoluteFilePath, // `string` - Example: '/var/icons/sub/dir/foo.svg'
122+
relativeFilePath, // `string` - Example: 'foo.svg'
123+
index // `number` - Example: `0`
124+
}) => [index, basename].join('_') // '0_foo'
117125
};
118126
```
119127

@@ -186,6 +194,8 @@ And the generated icon IDs would be:
186194
| `symbol-chevron-left` | `.icon.icon-chevron-left` |
187195
| `symbol-chevron-right` | `.icon.icon-chevron-right` |
188196

197+
You can provide a `getIconId` function via the configuration file to customize how the icon IDs / CSS selectors are derived from the filepath. The function will receive relative paths to the icon and the input directory as arguments, and must return a unique string to be used as the ID.
198+
189199
### Contribute
190200

191201
PRs are always welcome. If you need help questions, want to bounce ideas or just say hi, [join the Discord channel](https://discord.gg/BXAY3Kc3mp).

src/__mocks__/path.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const resolve = (...paths: string[]) => {
77
let path = '';
88

99
for (const cur of paths) {
10-
path = !path ? normalise(cur) : _path.join(path, normalise(cur));
10+
path = !path ? normalize(cur) : _path.join(path, normalize(cur));
1111
}
1212

1313
if (path.startsWith(projectDir)) {
@@ -22,11 +22,11 @@ const resolve = (...paths: string[]) => {
2222
}
2323
}
2424

25-
return normalise(path);
25+
return normalize(path);
2626
};
2727

2828
const relative = (a: string, b: string) =>
29-
normalise(_relative(_path.normalize(a), _path.normalize(b)));
29+
normalize(_relative(_path.normalize(a), _path.normalize(b)));
3030

3131
const join = (...segments: string[]): string => {
3232
const trimmed: string[] = [];
@@ -49,8 +49,8 @@ const join = (...segments: string[]): string => {
4949
return trimmed.join('/');
5050
};
5151

52-
const normalise = (path: string) => path.replace(/\\/g, '/');
52+
const normalize = (path: string) => path.replace(/\\/g, '/');
5353

5454
const isAbsolute = (path: string) => path.startsWith('/root');
5555

56-
module.exports = { resolve, relative, join, isAbsolute };
56+
module.exports = { resolve, relative, join, isAbsolute, normalize };

src/constants.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { resolve } from 'path';
22
import { RunnerOptions } from './types/runner';
33
import { FontAssetType, OtherAssetType } from './types/misc';
4+
import { getIconId } from './utils/icon-id';
45

56
export const TEMPLATES_DIR = resolve(__dirname, '../templates');
67

@@ -24,7 +25,8 @@ export const DEFAULT_OPTIONS: Omit<RunnerOptions, 'inputDir' | 'outputDir'> = {
2425
selector: null,
2526
tag: 'i',
2627
prefix: 'icon',
27-
fontsUrl: undefined
28+
fontsUrl: undefined,
29+
getIconId: getIconId
2830
};
2931

3032
export const DEFAULT_START_CODEPOINT = 0xf101;

src/core/__tests__/config-parser.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { parseConfig } from '../config-parser';
22
import { checkPath } from '../../utils/fs-async';
3+
import { DEFAULT_OPTIONS } from '../../constants';
34

45
const checkPathMock = (checkPath as any) as jest.Mock;
56

@@ -32,7 +33,8 @@ const mockConfig = {
3233
sass: 'sass',
3334
scss: 'scss',
3435
html: 'html'
35-
}
36+
},
37+
getIconId: DEFAULT_OPTIONS.getIconId
3638
};
3739

3840
const testError = async (options: object, key: string, message: string) =>
@@ -87,6 +89,7 @@ describe('Config parser', () => {
8789
'normalize',
8890
'must be a boolean value'
8991
);
92+
await testError({ getIconId: true }, 'getIconId', 'true is not a function');
9093
});
9194

9295
test('correctly validates existance of input and output paths', async () => {

src/core/__tests__/runner.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,11 @@ describe('Runner', () => {
114114
await generateFonts(optionsIn);
115115

116116
expect(loadAssetsMock).toHaveBeenCalledTimes(1);
117-
expect(loadAssetsMock).toHaveBeenCalledWith(inputDir);
117+
expect(loadAssetsMock).toHaveBeenCalledWith({
118+
...DEFAULT_OPTIONS,
119+
...optionsIn,
120+
parsed: true
121+
});
118122
});
119123

120124
test('`generateFonts` calls `getGeneratorOptions` correctly', async () => {

src/core/config-parser.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
parseDir,
55
parseString,
66
parseBoolean,
7+
parseFunction,
78
listMembersParser,
89
parseNumeric,
910
optional,
@@ -30,7 +31,8 @@ const CONFIG_VALIDATORS: {
3031
selector: [nullable(parseString)],
3132
tag: [parseString],
3233
prefix: [parseString],
33-
fontsUrl: [optional(parseString)]
34+
fontsUrl: [optional(parseString)],
35+
getIconId: [optional(parseFunction)]
3436
};
3537

3638
export const parseConfig = async (input: object = {}) => {

src/core/runner.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export const generateFonts = async (
4141
throw new Error('You must specify an output directory');
4242
}
4343

44-
const assetsIn = await loadAssets(options.inputDir);
44+
const assetsIn = await loadAssets(options);
4545
const generatorOptions = getGeneratorOptions(options, assetsIn);
4646
const assetsOut = await generateAssets(generatorOptions);
4747
const writeResults = outputDir ? await writeAssets(assetsOut, options) : [];

src/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ export {
22
FontAssetType,
33
OtherAssetType,
44
ASSET_TYPES,
5-
AssetType
5+
AssetType,
6+
GetIconIdFn,
7+
GetIconIdOptions
68
} from './types/misc';
79

810
export { RunnerOptions } from './types/runner';

src/types/misc.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,13 @@ export const ASSET_TYPES_WITH_TEMPLATE = [
2525
export const ASSET_TYPES = { ...FontAssetType, ...OtherAssetType };
2626

2727
export type AssetType = FontAssetType | OtherAssetType;
28+
29+
export interface GetIconIdOptions {
30+
basename: string;
31+
relativeDirPath: string;
32+
absoluteFilePath: string;
33+
relativeFilePath: string;
34+
index: number;
35+
}
36+
37+
export type GetIconIdFn = (options: GetIconIdOptions) => string;

src/types/runner.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { CodepointsMap } from '../utils/codepoints';
2-
import { FontAssetType, OtherAssetType, AssetType } from './misc';
2+
import { FontAssetType, OtherAssetType, AssetType, GetIconIdFn } from './misc';
33
import { FormatOptions } from './format';
44

55
export interface RunnerMandatoryOptions {
@@ -23,6 +23,7 @@ export type RunnerOptionalOptions = {
2323
templates: { [key in OtherAssetType]?: string };
2424
prefix: string;
2525
fontsUrl: string;
26+
getIconId: GetIconIdFn;
2627
};
2728

2829
export type RunnerOptionsInput = RunnerMandatoryOptions &

0 commit comments

Comments
 (0)