-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
@fluentui/react-icons: build performance & package bloat #25989
Comments
The problem with types affects also consumer side. In the project we have the whole set of icons bundled: import {
bundleIcon,
AccessTimeFilled,
AccessibilityFilled
} from "@fluentui/react-icons";
export const AccessTime: FluentIcon = /*#__PURE__*/ bundleIcon(
AccessTimeFilled,
AccessTimeRegular
);
/* all other icons */
/* ... */ The problem what we have is build performance: we use TS compiler to compile TS code and generate type definitions. tsc --extendedDiagnostics gives following:
Time
I run the same build with
At the same time I tried to collect CPU profile to understand what explodes: Current state
|
Other possible actions: Compress outputApply minifications to distributed files to compress JS output i.e. run Terser/SWC on Do not ship ESMAnother risky attempt might be to ship only CommonJS, here is a sandbox: https://stackblitz.com/edit/node-e7ki6x?file=index.js
While it works with Node:
I am not sure what will be an effect on bundlers 👀 Why not CJS entrypoint instead?// index.mjs
export const IconX = ''
export const IconY = '' // index.cjs
module.exports = await import('./index.mjs') This does not work as top level await works only in modules. Why not ESM only?Jest does not handle native ESM well, transpilation on consumer side will be required. |
I think this prevents bundlers from tree-shaking code. |
@spmonahan I updated the sandbox. Treeshaking works with Rollup at least, I believe that it could work with Webpack after some tweaks. |
Cool! I'd love to see how that works. |
I agree with layershifter's idea about |
No, don't unbundle the fonts. The entire purpose of the font work I did was so that Outlook (or any other bundling app concerned about performance) could convert all icons to the font versions, including those in our dependencies (who could be agnostic about SVG vs font). This was a big performance win for Outlook. Please set up time with us before undoing it. |
Also, if you're only going to ship one of ESM or CJS, why would you ever pick CJS? It's not statically analyzable, and webpack won't tree-shake it. |
Your work will not be removed, fonts should be just moved to a separate package (for example,
There is no agreement about that, it's one of proposals. Why pick CJS? Because it works in any environment, ESM code does not have good support in underlying tooling (hello Jest 🙃).
That's not true, Webpack supports tree shaking in CJS, Stackblitz demo |
No, I don't think you understand. If 1JS or Fluent or whatever are using the SVG package, and you unbundle the fonts from the package, there's no way for the bundler to coerce those SVG assets to fonts. Currently, that can be done with conditional exports: https://github.com/microsoft/fluentui-system-icons/tree/main/packages/react-icons#using-the-icon-font // webpack.config.js
module.exports = {
//...
resolve: {
conditionNames: ['fluentIconFont', 'require', 'node'],
},
}; |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, it's still unclear what do you mean. Can you please elaborate? For example, imports to a package could be replaced by a custom loader: -import { IconX } from '@fluentui/react-icons'
+import { IconX } from '@fluentui/react-font-icons' Or even use a Webpack alias. |
This comment was marked as off-topic.
This comment was marked as off-topic.
@svdoever This is not related to the problem described this issue. From the screenshot it's visible that you are using CommonJS output (should be ESM) in Webpack that is not expected and indicates a potential problem with your Webpack configuration. Please open a new issue and provide a minimal reproduction there, so we could take a look. |
Hey guys, I am facing the same problem. The new issue was created, after all? |
We moved over to Vite to build our bundles.
Met vriendelijke groet / regards,
Serge van den Oever
…________________________________
|
This is currently causing a blocking issue when using Fluent UI v9 with the Power Apps Component Framework (PAC). The size of the bundle with only a button and a label is reaching close to 14MB, almost entirely from the icons and fonts. The size is so large that the PAC CLI tools and PCF scripts are unable to upload the code component bundle to Power Apps. In other words, it is currently not possible to use Fluent UI v9 with the PAC CLI tools due to the bundle size being too large. The workaround until this is resolved is to use Fluent UI v8 (or maybe there is some hack with webpack since the PCF scripts use it to bundle). Technical issues aside, the icons and fonts are covered under a different license. Most of the repository is MIT, except the icons and fonts. This creates a lack of consistency and some legal ambiguity about the use of the icons and fonts as well as the mandatory inclusion of the SVG icons with the bundle. According to the Microsoft Fabric Assets License Agreement, download of the icons and fonts constitutes agreement to the license terms which restricts the use of the icons and fonts:
Although I doubt Microsoft will enforce use of the Segoe UI font for only Microsoft services or applications, it muddies the water. Having the icons and fonts in a separate repository or package would resolve both the issues; The icon and font coupling issue creating massive bundles which is in turn prevents PAC code components from using Fluent UI v9 without hacks, and the ambiguous licensing issue. |
How are you bundling your app? It sounds like you do no tree-shaking at all, if using a single button pulls in the entire package, which is a much bigger issue for you than just this package. |
@MLoughry using the pcf-scripts with a project initialized via There is an official Microsoft guide that uses Fluent UI v8. Component Framework: Tutorial Create Canvas Dataset Component Unfortunately, the official pcf-scripts package is not an open-source project or hosted publicly on GitHub. |
I followed the comment from microsoft/fluentui-system-icons#619 (comment) I was using I changed it to:
and it changed my webpack bundle size from 14MB down to 1.7MB. |
That worked! Although ironically topical that the answer was switching from cjs to ESM for the module output. I also tried using |
jest/unit-tests are indeed affected #31714 (comment)
|
Library
React Components / v9 (@fluentui/react-components) & @fluentui/react-icons.
Current state with tooling
Webpack
In development
To be done
In production
Tree-shaking works ✅
Bundles reasonably fast (webpack 5.75.0 on Apple M1 in 1857 ms, 2048 ms) ✅
CodeSandbox
non cloud
Works surprisingly well (even with 4x CPU slowdown):
https://codesandbox.io/s/naughty-paper-14wujx?file=/src/App.js
Uses CJS and loads all chunks for every icon⚠️
cloud beta
Works even better
https://codesandbox.io/p/sandbox/priceless-knuth-m8e6iz
Loads bundled ESM by Vite (around 2.1mb)
TypeScript
Used TS 4.9.4.
Builds a fixture reasonably fast with
skipLibCheck: true
&skipLibCheck: false
✅Next.js
Used Next.js 13.0.6.
Rebuilds in dev are reasonably fast. Builds, too (on Apple M1 12.37s with icons, 8.67s without).
create-react-app
In development
To be done
In production
It's still Webpack under hood, but they process
node_modules
with Babel loader 💥build
17.07s with iconsbuild
3.30s without iconsbuild
4.51s with iconsbuild
3.33s without iconsJest
Does not seem that icons affect it in any way.
Package size
It's where things are getting worse. react-icons-2.0.190.tgz downloadable size is 15.1mb, unpacked size is 71mb 💥
Fonts
It's a big problem actually as it double package twice.
Action 1 (mandatory): glyphs should be present once
Currently glyphs are present twice for CJS & ESM 💣 However, fonts are not modules and a JSON file next them is also not a module.
https://www.jsdelivr.com/package/npm/@fluentui/react-icons?path=lib%2Futils%2Ffonts
https://www.jsdelivr.com/package/npm/@fluentui/react-icons?path=lib-cjs%2Futils%2Ffonts
If we will have a single instance we go down to 63mb (-8mb!) unpacked.
Action 2: remove fonts completely
Move fonts and related JS to a separate package:
While the plugin looks on a relative path - I don't think that it's a good enough reason to keep ghyphs and slow down installs for everyone.
Deleting fonts completely moves us down to 41mb.
Types
Duplicate types
Currently we have separate types for ESM and CJS:
It means that we duplicate definitions for a no reason as the
package.json
references onlylib/index.d.ts
and we have export maps that prevent deep imports.Duplicate types cost 5mb.
Ideally, it would be great to rollup them.
Expanded definitions ✅
We have this for every icon, types are inlined, not great at all.
Killing a generic there makes types more accurate:
Ideally, it should be just:
Usage of a type without
displayName
probably will lead to better results. Just the change above saves 3mb.Minify output
To be done.
Requested priority
High
The text was updated successfully, but these errors were encountered: