nivekcode / svg-to-ts

Build performant SVG icon libraries by converting raw SVG files to TypeScript that is optimized for modern tree shaking mechanisms.
MIT License
275 stars 45 forks source link

Unable to reproduce fully treshakeable icon library #65

Closed glenngr closed 4 years ago

glenngr commented 4 years ago

I've been trying to follow the steps outlined in the README and the Guide on Medium, but I'm unable to get a fully tree-shakeable library.

All icons always end up in the main bundle, even when I clone the PoC repo .

I have been able to create a fully tree-shakeable icon lib, but it requires additional steps. I had to configure ng-packagr to treat each icon as a secondary entry point.

I moved each icon.ts file into its own directory with the following structure

my_icon_package
├── src
|   ├── public_api.ts
|   └── icon.model.ts
├── ng-package.json
├── package.json
└── icons
    ├── svg-icon-home
    |   ├── src
    |   |   ├── public_api.ts
    |   |   └── svg-icon-home.ts
    |   └── package.json
    ├── svg-icon-back
    |   ├── src
    |   |   ├── public_api.ts
    |   |   └── svg-icon-back.ts
    |   └── package.json
    ├── svg-icon-forward
    |   ├── src
    |   |   ├── public_api.ts
    |   |   └── svg-icon-forward.ts
    |   └── package.json

public_api exports the single icon file and package json just contains

{
    "ngPackage": {}
}

Now when I try to build my lib, it actually builds each icon seperately.

Now I have to import each icon from its own secondary entry point Before: import { mySvgIcon } from '@myorg/shared/ui/icons';

now: `import { mySvgIcon } from '@myorg/shared/ui/icons/my-svg-icon';

This produces the same result you have gotten in your article.

Is this expected or has something changed?

Should support for a new generator "packages" be added?

The setup I have used actually mimics what is being done in the @angular/components and @angular/angular repo

Sample build output:

> ng run shared-ui-icons:build:production 
Building Angular Package

------------------------------------------------------------------------------
Building entry point '@myorg/shared-ui-icons'
------------------------------------------------------------------------------
Compiling TypeScript sources through ngc
Bundling to FESM2015
Bundling to FESM5
Bundling to UMD
Minifying UMD bundle
Writing package metadata
Built @myorg/shared-ui-icons

------------------------------------------------------------------------------
Building entry point '@myorg/shared-ui-icons/src/icons/svg-access-point-network'
------------------------------------------------------------------------------
Compiling TypeScript sources through ngc
Bundling to FESM2015
Bundling to FESM5
Bundling to UMD
Minifying UMD bundle
Writing package metadata
Built @myorg/shared-ui-icons/src/icons/svg-access-point-network

------------------------------------------------------------------------------
Building entry point '@myorg/shared-ui-icons/src/icons/svg-access-point'
------------------------------------------------------------------------------
Compiling TypeScript sources through ngc
Bundling to FESM2015
Bundling to FESM5
Bundling to UMD
Minifying UMD bundle
Writing package metadata
Built @myorg/shared-ui-icons/src/icons/svg-access-point

------------------------------------------------------------------------------
Building entry point '@myorg/shared-ui-icons/src/icons/svg-account-alert'
------------------------------------------------------------------------------
Compiling TypeScript sources through ngc
Bundling to FESM2015
Bundling to FESM5
Bundling to UMD
Minifying UMD bundle
Writing package metadata
Built @myorg/shared-ui-icons/src/icons/svg-account-alert
glenngr commented 4 years ago

Here is my svg-to-ts config

    "svg-to-ts": {
        "srcFiles": [
            "./libs/shared/ui/icons/svg/**/*.svg"
        ],
        "outputDirectory": "./libs/shared/ui/icons/src",
        "interfaceName": "Icon",
        "typeName": "SvgIcon",
        "prefix": "svg",
        "iconsFolderName": "lib",
        "optimizeForLazyLoading": true,
        "modelFileName": "svg-icon.model",
        "additionalModelFile": "./libs/shared/ui/icons/src",
        "conversionType": "files",
        "delimiter": "KEBAB",
        "compileSources": false,
        "svgoConfig": {
            "plugins": [
                {
                    "cleanupAttrs": true
                }
            ]
        }
    }
glenngr commented 4 years ago

I've forked the repo and built a prototype ng-package converter. Let me know if you want to take a look. I can open a PR

nivekcode commented 4 years ago

Hi @glenngr

Thanks a lot for this issue. Curious why it didn't work. If I find some time I will try to invest why the sample repo did not work. I would definitely be interested in the ng-package converter you created.

nivekcode commented 4 years ago

Hi, @glenngr I researched a bit why this doesn't work for you and I figured out that there is still something wrong in the description of my post. Sorry for that 😥

The issue is that the showcase I was using used the library from the projects folder instead of the dist folder. In such cases, the output is different because we are missing the library build step. The library is built as part of the showcase build an therefore the tree shaking is working.

However, I also used svg-to-ts in a big enterprise client, and there tree shakable icons are working. Basically you need to let svg-to-ts handle the icons and not ng-packagr. ng-packagr produces one big bundle with all icons that break tree shaking. To let svg-to-ts handle the icon compilation you have two options:

  1. Provide the icons as a standalone library. With this approach icons are provided standalone and can be used framework agnostic throughout the various projects. If you consider this approach I would recommend you to check out the starter project we added in the linked PR to the README svg-icon-library-starter.
  2. If you want to have your icons in an Angular workspace you should treat this folder as an assets folder. So basically, put your icons in there, exclude it from the ng build and use svg-to-ts to build the icons into the dist folder.
glenngr commented 4 years ago

@kreuzerk Thanks for answering. It works fine when the files from the dist folder. I'm not sure the ng-packagr converter I mentioned earlier is needed now. Let me know if you want to have a look anyway. It basically just creates one package subentry for each icon, but the build process is much slower compared to the way you do it now.

I did experiment with creating an angular builder for svg-to-ts, making you able to just run ng run icons:build to produce the tree-shakeable icon files.

The cool thing about it is that if you use it inside an Nx Workspace, nx can cache the build output and you can even configure nx to know that it has to rebuild the library if a new svg-file is added to the svg-directory or if the svg-to-ts configuration changes in package.json.

I think I also want to create some angular-cli schematics to set up the projects you need to create an icon library using svg-to-ts.

Do you think this belongs in a separate project, or would you be interested in having it as part of this repo?

nivekcode commented 4 years ago

Hi @glenngr glad to hear that it is working now. 👍

I really like the idea of having an Angular builder. In fact, we already have an issue with that. Also, a possible usage with Nx sounds very interesting. The same goes for the schematics to scaffold a library project.

Generally, I think that the Angular builder and the schematics should be in a dedicated project. I imagine the builder as a sort of wrapper around svg-to-ts. The reason why I think it should be in a separate project is that svg-to-ts can also be used by other projects that use Vanilla JS, TypeScript, Vue, React etc...

Although, it would be nice to have everything somehow grouped together. Maybe we could think of adding an organization and put all projects to it.

KingDarBoja commented 3 years ago

I am facing the same issue if I try to keep the library as Angular one as tree-shaking works with "conversionType": "files" but unfortunately looks like to achieve tree-shaking + lazy-loading it requires either having 1 secondary entry point per TS icon or copy the compiled sources after building the library as @kreuzerk pointed out. The downside of the second approach is trying to figure out how to alias the import paths in a nx workspace.