Skip to content

Commit

Permalink
feat(converting): add new convertion option to convert svg-icons to o…
Browse files Browse the repository at this point in the history
…bjects
  • Loading branch information
nivekcode committed Jun 15, 2020
1 parent aec29ac commit 9459f36
Show file tree
Hide file tree
Showing 8 changed files with 227 additions and 70 deletions.
48 changes: 36 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ are made with Angular, however `svg-to-ts` can also be used with other framework
- `svg-to-ts` optimizes your SVG icons under the hood
- `svg-to-ts` automatically generates types and interfaces for your icons to improve typesafety
- `svg-to-ts` was developed based on the experiences of providin an icon library for a large enterprise.
- highly configurable - supports multiple use cases.
- offers three different convertion modes ('object', 'single-file' and 'multiple-files')
- each method is highly configurable to supports multiple use cases.

# How to use svg-to-ts

Expand Down Expand Up @@ -78,6 +79,7 @@ Once you run `svg-to-ts` those configurations will be picked up.
"generate-icons": "svg-to-ts"
},
"svg-to-ts": {
"convertionType": "single-file",
"srcFiles": ["./projects/dinosaur-icons/icons/**/*.svg"],
"outputDirectory": "./projects/dinosaur-icons/icons",
"interfaceName": "DinosaurIcon",
Expand Down Expand Up @@ -107,6 +109,7 @@ Once you run `svg-to-ts` those configurations will be picked up.
```json
{
"svg-to-ts": {
"convertionType": "single-file",
"srcFiles": ["./projects/dinosaur-icons/icons/**/*.svg"],
"outputDirectory": "./projects/dinosaur-icons/icons",
"interfaceName": "DinosaurIcon",
Expand Down Expand Up @@ -139,19 +142,39 @@ If you decide to configure `svg-to-ts` by using a `.rc` file, it still makes sen
}
```

## Use-cases
## ConvertionTypes

svg-to-ts offers three different kinds of convertion types; Converting your icons to a single object,
converting your icons to constants or converting your icons to single files. Each approach is designed
to solve a specific kind of problem. You can switch between approaches by passing `convertionType` property (`object`, `single-file` or `multiple-files`).

### 1. Converting to a single object (`convertionType==='object'`)

In this scenario the SVG icons are converted to a single object. It's an approach that is suitable if your icon registry
accepts an object with the filename as key and the svg data as key.

Available options:

| --version | type | default | output the version number |
| --------------- | ----------------------- | ---------------------------------------- | ---------------------------------------------------------------------------- |
| fileName | stirng | my-icons | file name of the generated file |
| delimiter | CAMEL, KEBAP, SNAKE | SNAKE | delimiter which is used to generate the types and name properties |
| svgoConfig | string or config object | check help command - to large to display | a path to your svgoConfiguration JSON file or an inline configuration object |
| srcFiles | string | "/\*.svg" | input files matching the given filename pattern |
| outputDirectory | string | "./dist" | name of the output directory |

As mentioned above, `svg-to-ts` supports different use-cases. You can either generate you library to a single TypeScript file with multiple constants, to single TypeScript file per Icon
or to allready precompiled JavaScript files.

### Use Case 1 - Treeshakable and typesafe with one file (simpler use cases)
### 2. Multiple constants - Treeshakable and typesafe with one file (`convertionType==='single-file'`)

This approach converts your svg icons into multiple constants in the same file so that they can be used
in combination with an icon registry. It furthermore also generates all necssary types. **We wrote a step to step guide that explains this approach further and helps you create an icon library with this approach.**
[Find out more in this blogpost](https://medium.com/angular-in-depth/how-to-create-an-icon-library-in-angular-4f8863d95a)

![Output scenario one](https://raw.githubusercontent.com/kreuzerk/svg-to-ts/master/assets/example-src1.png)
Only the icons included in the consuming SPA also end up in the final bundle of the SPA.

**We wrote a step to step guide that explains this approach further and helps you create an icon library with this approach.**
[Find out more in this blogpost](https://medium.com/angular-in-depth/how-to-create-an-icon-library-in-angular-4f8863d95a)

Available configurations:

| --version | type | default | output the version number |
Expand All @@ -166,7 +189,6 @@ Available configurations:
| svgoConfig | string or config object | check help command - to large to display | a path to your svgoConfiguration JSON file or an inline configuration object |
| srcFiles | string | "/\*.svg" | input files matching the given filename pattern |
| outputDirectory | string | "./dist" | name of the output directory |
| outputDirectory | string | "./dist" | name of the output directory |

#### Example usage

Expand All @@ -183,7 +205,12 @@ and we end up with the following file in our `dist` folder.

![output](https://raw.githubusercontent.com/kreuzerk/svg-to-ts/master/assets/output.png)

### Use Case 2 - Fully tree shakable and optimized for lazy loading (more sophisticated)
### 3. Fully tree shakable and optimized for lazy loading (`convertionType==='multiple-files'`)

This is the most sophisticated approach and also the approach that doesn't only support tree shaking but also
supports code splitting which is especially usefull in scenarios where you are using lazy loading.

[Here's a step by step guide on how to create an icon library that is optimized for tree shaking](https://medium.com/angular-in-depth/how-to-create-a-fully-tree-shakable-icon-library-in-angular-c5488cf9cd76)

![fully tree shakable](https://raw.githubusercontent.com/kreuzerk/svg-to-ts/master/assets/fully-treeshakable.png)
Often, having the SVGs in a single file is enough. However if you are in a more complex environment with bigger business
Expand All @@ -196,8 +223,6 @@ gets reduced, but also, where they end up. Means, an icon that is only used in a
end up there.
![Output scenario two](https://raw.githubusercontent.com/kreuzerk/svg-to-ts/master/assets/generated-files-src2.png)

[Here's a step by step guide on how to create an icon library that is optimized for tree shaking](https://medium.com/angular-in-depth/how-to-create-a-fully-tree-shakable-icon-library-in-angular-c5488cf9cd76)

Available configurations:

| --version | type | default | output the version number |
Expand All @@ -212,7 +237,6 @@ Available configurations:
| srcFiles | string | "/\*.svg" | input files matching the given filename pattern |
| svgoConfig | string or config object | check help command - to large to display | a path to your svgoConfiguration JSON file or an inline configuration object |
| outputDirectory | string | "./dist" | name of the output directory |
| outputDirectory | string | "./dist" | name of the output directory |
| optimizeForLazyLoading | boolean | false | when set to true, multiple files will be generated |
| additionalModelOutputPath | string | null | if a path is specified we will generate an additional file containing interface and type to this path - can be useful to improve type safety |
| iconsFolderName | string | "build" | name of the folder we will build the TypeScript files to |
Expand All @@ -222,7 +246,7 @@ Available configurations:

## Which approach should I use

This depends on your use case. If you have a simple application, it's probably enought to go with the single file and the constants.
This depends on your use case. If you have a simple application, it's probably enought to go with the single file or even a object.
If you build a framework that is used by multiple teams, then you should probably go with the fully tree shakable scenario (generating multiple files).

## Is it possilbe to create a standalone library?
Expand Down
24 changes: 20 additions & 4 deletions src/bin/svg-to-ts.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,34 @@
#!/usr/bin/env node
import { convertToSingleFile } from '../lib/converters/single-file.converter';
import { convertToMultipleFiles } from '../lib/converters/multiple-files.converter';
import { getOptions, MultiFileConvertionOptions, SingleFileConvertionOptions } from '../lib/options/convertion-options';
import { printLogo } from '../lib/helpers/log-helper';
import {
ConvertionType,
getOptions,
MultiFileConvertionOptions,
ObjectConvertionOptions,
SingleFileConvertionOptions
} from '../lib/options/convertion-options';
import { info, printLogo } from '../lib/helpers/log-helper';
import { setupCommander } from '../lib/options/args-collector';
import { convertToSingleObject } from '../lib/converters/object.converter';

(async () => {
setupCommander();
printLogo();
const convertionOptions = await getOptions();

if (convertionOptions.optimizeForLazyLoading) {
if (convertionOptions.convertionType === ConvertionType.MULTIPLE_FILES) {
info('We are using the convertiontype "multiple-files"');
await convertToMultipleFiles(convertionOptions as MultiFileConvertionOptions);
} else {
}

if (convertionOptions.convertionType === ConvertionType.SINGLE_FILE) {
info('We are using the convertiontype "single-file"');
await convertToSingleFile(convertionOptions as SingleFileConvertionOptions);
}

if (convertionOptions.convertionType === ConvertionType.OBJECT) {
info('We are using the convertiontype "single-object"');
await convertToSingleObject(convertionOptions as ObjectConvertionOptions);
}
})();
20 changes: 20 additions & 0 deletions src/lib/converters/object.converter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ObjectConvertionOptions } from '../options/convertion-options';
import { writeFile } from '../helpers/file-helpers';
import { error, success, underlineSuccess } from '../helpers/log-helper';

import { filesProcessor, SvgDefinition } from './shared.converter';

export const convertToSingleObject = async (convertionOptions: ObjectConvertionOptions): Promise<void> => {
const { outputDirectory, fileName } = convertionOptions;
try {
const svgObject = {};
const svgDefinitions = await filesProcessor(convertionOptions);
svgDefinitions.forEach(
(svgDefinition: SvgDefinition) => (svgObject[svgDefinition.filenameWithoutEnding] = svgDefinition.data)
);
await writeFile(outputDirectory, fileName, `export const icons = ${JSON.stringify(svgObject)}`);
success(`Icons file successfully generated under ${underlineSuccess(outputDirectory)}`);
} catch (exception) {
error(`Something went wrong: ${exception}`);
}
};
2 changes: 1 addition & 1 deletion src/lib/converters/single-file.converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const getSvgConstants = svgDefinitions => {
};

export const convertToSingleFile = async (convertionOptions: SingleFileConvertionOptions): Promise<void> => {
const { typeName, interfaceName, outputDirectory, fileName } = convertionOptions;
const { outputDirectory, fileName } = convertionOptions;
try {
const svgDefinitions = await filesProcessor(convertionOptions);
if (svgDefinitions.length) {
Expand Down
59 changes: 54 additions & 5 deletions src/lib/options/args-collector.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import commander from 'commander';
import { MultiFileConvertionOptions, SingleFileConvertionOptions } from './convertion-options';
import { DEFAULT_OPTIONS } from './default-options';

import * as packgeJSON from '../../../package.json';
import { Delimiter } from '../generators/code-snippet-generators';
import { getSvgoConfig } from '../helpers/svg-optimization';

import {
MultiFileConvertionOptions,
SingleFileConvertionOptions,
ObjectConvertionOptions,
ConvertionType
} from './convertion-options';
import { DEFAULT_OPTIONS } from './default-options';
import { error } from '../helpers/log-helper';

export const setupCommander = () => {
const collect = (value, previous) => previous.concat([value]);
commander
.version(packgeJSON.version)
.option('-t --typeName <string>', 'name of the generated enumeration type', DEFAULT_OPTIONS.typeName)
.option('-t --convertionType <ConvertionType>', 'convetion type (object, single-file, multiple-files)')
.option('--objectName <string>', 'name of the exported object', DEFAULT_OPTIONS.objectName)
.option('--typeName <string>', 'name of the generated enumeration type', DEFAULT_OPTIONS.typeName)
.option('--generateType <boolean>', 'prevent generating enumeration type', DEFAULT_OPTIONS.generateType)
.option('--generateTypeObject <boolean>', 'generate type object', DEFAULT_OPTIONS.generateTypeObject)
.option('-f --fileName <string>', 'name of the generated file', DEFAULT_OPTIONS.fileName)
Expand Down Expand Up @@ -69,8 +79,18 @@ const toBoolean = (str: string, defaultValue: boolean): boolean => {
return result;
};

export const collectArgumentOptions = async (): Promise<SingleFileConvertionOptions | MultiFileConvertionOptions> => {
export const collectArgumentOptions = async (): Promise<
SingleFileConvertionOptions | MultiFileConvertionOptions | ObjectConvertionOptions
> => {
if (!commander.convertionType) {
error(`A convertionType is required, please specify one by passing it via --convertionType.
Valid convertiontypes are (object, single-file or multiple-files)`);
process.exit();
}

let {
convertionType,
objectName,
delimiter,
fileName,
interfaceName,
Expand Down Expand Up @@ -104,9 +124,38 @@ export const collectArgumentOptions = async (): Promise<SingleFileConvertionOpti
svgoConfig = await getSvgoConfig(svgoConfig);
}

if (convertionType === ConvertionType.OBJECT) {
return {
convertionType,
delimiter,
srcFiles,
outputDirectory,
svgoConfig,
fileName,
objectName
};
}

if (convertionType === ConvertionType.SINGLE_FILE) {
return {
convertionType,
delimiter,
fileName,
interfaceName,
srcFiles,
outputDirectory,
prefix,
typeName,
generateType,
generateTypeObject,
svgoConfig,
optimizeForLazyLoading
};
}

return {
convertionType,
delimiter,
fileName,
interfaceName,
srcFiles,
outputDirectory,
Expand Down
Loading

0 comments on commit 9459f36

Please sign in to comment.