-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
[icons] Enable tree-shaking for SVG icons #2356
Conversation
Bail if icon name is invalidPreview: documentation | landing | table |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎆 @reiv this is some great stuff! i'm mildly uncomfortable with the SVGIcon
type and the functionIcon
naming scheme, but i see why you went that route. i wonder what it would take to have FunctionIcon
be an actual JSX element?
config/tsconfig.base.json
Outdated
@@ -24,6 +24,7 @@ | |||
"removeComments": false, | |||
"sourceMap": false, | |||
"stripInternal": true, | |||
"target": "es5" | |||
"target": "es5", | |||
"typeRoots": ["../node_modules/@types"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this necessary now? we've never needed it in the past.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was just something I had to add on my end because of a bug with at-loader
on Windows. In particular, it was causing Typescript to complain about being unable to find require()
. There's some discussion of it here: PatrickJS/PatrickJS-starter#1371
It shouldn't affect anything else, but happy to split this off into a separate PR to reduce noise in the diff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmmm core
declares the require()
type. we should probably just do the same in icons rather than changing the tsconfig.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However, this also fixes a slew of errors that occur as a result of that at-loader
+ Windows interaction. For example, it doesn't pick up the typings for Mocha, so compiling unit tests gives me a wall of can't find "it/describe/etc"
errors. I'll definitely submit this as a separate PR then.
ICONS_METADATA.map(async icon => { | ||
const filepath = path.resolve(__dirname, `../../resources/icons/${size}px/${icon.iconName}.svg`); | ||
const svg = fs.readFileSync(filepath, "utf-8"); | ||
const pathStrings = await svgo | ||
.optimize(svg, { path: filepath }) | ||
.then(({ data }) => data.match(/ d="[^"]+"/g) || []) | ||
.then(paths => paths.map(s => s.slice(3))); | ||
return ` "${icon.iconName}": [${pathStrings.join(",\n")}],`; | ||
paths[icon.iconName] = `[${pathStrings.join(",\n")}]`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is now a reduce
, not a map
.
ICONS_METADATA.reduce(async (paths, icon) => { ... }, {})
*/ | ||
function buildPathsObject(paths) { | ||
return Object.keys(paths).map(iconName => { | ||
return ` "${iconName}": ${paths[iconName]},` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
inline! iconName => "template-string"
@giladgray If
Open to exploring this option as well (I was thinking about it too). All it would take is exporting the icons as SFCs or something; basically a function that returns an |
Refactor for code stylePreview: documentation | landing | table |
let's avoid the main reason |
}), | ||
); | ||
paths[icon.iconName] = `[${pathStrings.join(",\n")}]`; | ||
return paths; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
something weird going on with whitespace here?
@giladgray it'll be hard to pursue the individual-icons-as-Components idea if |
Fix whitespacePreview: documentation | landing | table |
This reverts commit c45e459.
A few more thoughts following up on the above:
I'll probably try the former first and see where it takes us (can always revert if it's a no-go.) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looking good! i'm gonna have to check this out locally and play with it before final sign-off.
packages/icons/src/common/props.ts
Outdated
@@ -0,0 +1,71 @@ | |||
/* | |||
* Copyright 2015 Palantir Technologies, Inc. All rights reserved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please update all copyrights to 2018
.then(({ data }) => data.match(/ d="[^"]+"/g) || []) | ||
.then(paths => paths.map(s => s.slice(3))); | ||
paths[icon.iconName] = `[${pathStrings.join(",\n")}]`; | ||
return paths; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indentation?
public static displayName = "Blueprint2.Icon"; | ||
|
||
public static readonly SIZE_STANDARD = 16; | ||
public static readonly SIZE_LARGE = 20; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reference the SVGIcon
constants instead of redeclaring constants
packages/core/src/common/errors.ts
Outdated
@@ -26,6 +26,11 @@ export const HOTKEYS_WARN_DECORATOR_NO_METHOD = ns + ` @HotkeysTarget-decorated | |||
export const HOTKEYS_WARN_DECORATOR_NEEDS_REACT_ELEMENT = | |||
ns + ` "@HotkeysTarget-decorated components must return a single JSX.Element or an empty render.`; | |||
|
|||
export const ICON_STRING_NAMES_NOT_SUPPORTED = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this used in core
package? pretty sure it's safe to delete as it's defined in icons
below.
|
||
let allIcons: { [key: string]: IconPartial | undefined } | null = null; | ||
|
||
if (!(global as any).BLUEPRINT_ICONS_TREE_SHAKING) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 if a user never imports this component and only uses <NamedIcon />
exports, then will this file (and allIcons
) be tree-shook automatically? as in, do we actually need this flag or can we rely on the build tool to detect that this module is unused?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But Icon
is still used under the hood in other components, so it's unlikely that it's never imported.
@giladgray I'll try to address your code comments later today. As for that global flag, I'm quite certain it's still needed. The default behavior is to pull in all icon components when importing Edit: lint rule looks good by the way. An auto-fixer would be the icing on top of course! |
Move component-specific props out of commonPreview: documentation | landing | table |
@giladgray Just letting you know that I won't have the time to work on this PR for the next few weeks due to other commitments. Sorry about that! Please feel free to take over from here (or cherry-pick into your own branch). |
@reiv thanks for the update! i'm sorry for not having had the time to properly review this, but i will certainly make sure it gets released soon, likely as part of 3.0 |
@giladgray Any progress with this PR? |
@jonavila nope it's not a priority for us internally (we have bigger fish to tree-shake) and it can be done in a non-breaking way so not critical for 3.0.0. there may be some progress soon 😄 |
What do you mean with |
@jonavila we as a company use the icons widely and frequently in a parameterized way (hence the |
@giladgray Totally get that. I think your answer was directed to @johnunclesam |
ah yep it was, thanks 🌴 |
Breaking change |
gonna close this PR because woo boy is it out of date! |
It is out of date, ok, @giladgray, but is tree-shaking for SVG icons a thing in blueprint today? |
@frederikhors nope, sorry. this feature is sadly not a priority for the team. |
Fixes #2193
Changes proposed in this pull request:
This PR restructures the icons package to enable tree shaking for SVG icons. This should reduce the build size in production by quite a bit (around 350-400 KiB). Because the current icons architecture is incompatible with tree shaking, this optimization needs to be enabled with a compile flag, tentatively named
BLUEPRINT_ICONS_TREE_SHAKING
. Enabling this flag means that theicon
prop of Icon components can no longer be specified as a string, and prevents the entirety of theicons
package from being bundled by default. Instead, it falls to the consumer to manually import whichever icons are needed in that project. SVG icons are exported at the root level of theicons
package, using aliases that arecamelCasedPascalCased versions of the icon names suffixed with "Icon". For example:Some Webpack build sizes:
The tree shaking flag can be set using webpack's DefinePlugin as follows:
This will break everything that uses string-form icon props (nothing has been ported over yet).