Skip to content
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

[EuiIcon] Convert generated icon .js assets to .tsx files #5212

Merged
merged 21 commits into from
Oct 4, 2021

Conversation

cee-chen
Copy link
Contributor

@cee-chen cee-chen commented Sep 23, 2021

Summary

A nice capstone to my #5044 work - with this PR, all .js files in src/ are officially gone! 💥

I recommend following along by commit, and skimming commits that indicate they're primarily running yarn compile-icons 😅 I also left some code comments walking through specific changes, w/ documentation links.

QA

Checklist

  • Check against all themes for compatibility in both light and dark modes

- [ ] Checked in mobile

  • Checked in Chrome, Safari, Edge, and Firefox

- [ ] Props have proper autodocs and playground toggles
~- [ ] Added documentation
- [ ] Checked Code Sandbox works for any docs examples

- [ ] Checked for breaking changes and labeled appropriately

  • Checked for accessibility including keyboard-only and screenreader modes
  • A changelog entry exists and is marked appropriately

- to get the latest features and perf improvements
- xmlns attr location changed for some reason :|
- looks like it added xlink
- to match fs.readFileSync and .writeFileSync
- now that the files are .tsx, it's linting for the license

NOTE: I could have added a eslint --fix command to yarn compile-icons, but doing so added another 30-40s of runtime to the command. Doing it in the writeFileSync keeps the command around 5-6s instead.
- by leaving a hint as to what file generator name to open/look for

- now that these are actual .tsx files, it's going to be even less obvious to not edit them directly, and it's a nice developer affordance to say which file to look at rather than having to grep through the repo
package.json Show resolved Hide resolved
@@ -27,7 +30,7 @@ iconFiles.forEach(async (filePath) => {

const hasIds = svgString.includes('id="');

let jsxSource = await svgr(
let jsxSource = svgr.sync(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional refactor, I just saw the documentation for it in https://react-svgr.com/docs/node-api/#usage and thought .sync matched fs.readFileSync and fs.writeFileSync better

@@ -42,15 +45,17 @@ iconFiles.forEach(async (filePath) => {
xmlns: 'http://www.w3.org/2000/svg',
},
titleProp: true,
typescript: true,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scripts/compile-icons.js Show resolved Hide resolved
const comment = '// THIS IS A GENERATED FILE. DO NOT MODIFY MANUALLY\n\n';
fs.writeFileSync(outputFilePath, comment + jsxSource);
const outputFilePath = filePath.replace(/\.svg$/, '.tsx');
const comment = `\n// THIS IS A GENERATED FILE. DO NOT MODIFY MANUALLY. @see scripts/compile-icons.js\n\n`;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional generated comment enhancement: now that the icon files are .tsx, it's even less obvious that you shouldn't edit them, so I figured it's nice to provide a @see link and filename for devs to jump to directly rather than having to grep through the repo

scripts/compile-icons.js Show resolved Hide resolved
@@ -730,7 +730,7 @@ exports[`EuiIcon props type arrowDown is rendered 1`] = `
>
<path
d="M13.069 5.157L8.384 9.768a.546.546 0 01-.768 0L2.93 5.158a.552.552 0 00-.771 0 .53.53 0 000 .759l4.684 4.61c.641.631 1.672.63 2.312 0l4.684-4.61a.53.53 0 000-.76.552.552 0 00-.771 0z"
fill-rule="non-zero"
fill-rule="nonzero"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typescript caught this attribute issue, proving it works 🎉

@cee-chen cee-chen marked this pull request as ready for review September 23, 2021 19:50
@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_5212/

@thompsongl
Copy link
Contributor

I tested this out in a simple create-react-app project, and icons fail to load:

Screen Shot 2021-09-28 at 2 48 20 PM

And thinking about this all again, that makes sense. The icon files will get compiled to js during the build, so the tsx lookup doesn't find the file.
It'll work in the docs because of how webpack is used (I'm guessing the dist/ bundles work also), but not in the standard babel builds (lib/ and es/)

@cee-chen
Copy link
Contributor Author

Ahhh 🤦‍♀️ Thanks for catching that Greg! I wonder if there's a way to detect "if compiled, looking for .js, if not, look for .tsx"... or falling back to .js if the .tsx file doesn't exist?

@cee-chen
Copy link
Contributor Author

Or alternatively - is there a strong reason to import/load icons dynamically in the first place? Why not import them statically first and then find the right one via name/map later? 🤷

IIRC, tree-shaking isn't yet working in EUI/webpack so it's not really for perf reasons there, right?

@thompsongl
Copy link
Contributor

A build-aware or try/catch solution sounds better, if possible. Even though treeshaking doesn't currently work, it's something we want eventually.

- webpack should be smart enough to attempt to load either a .tsx or a .js file automatically
@cee-chen
Copy link
Contributor Author

cee-chen commented Sep 29, 2021

@thompsongl So, earlier this morning I went down a 30min+ rabbit hole of seeing if I could convert the dynamic imports into static, and when I finally had it working and was doing some research on the perf implications, I had this epiphany...

"Why are we even specifying either .js or .tsx?" 🤦‍♀️

I'm 90% sure Webpack is typically smart enough to automatically import either TSX or JS modules over an .svg file (source, particularly the last 2 paragraphs). Removing the file extension completely works for me on local docs, and it also appears to be working for me in a local CRA app (although my setup could be off here). Worst comes to worst if for some reason it's struggling with the same file name existing as both .svg or .js/tsx, I can just split up the source SVGs into a different folder than the output .tsx files.

Let me know if the latest commit works for you! I tested on local Kibana dev as well and it appears to be working there 🤞

@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_5212/

@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_5212/

@thompsongl
Copy link
Contributor

Lots of warnings in the build log related to .svg lookups. Webpack might be trying to load both files?

13:21:45 WARNING in ./components/icon/assets/tokens/tokenCompletionSuggester.svg 1:0
13:21:45 Module parse failed: Unexpected token (1:0)
13:21:45 You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
13:21:45 >
13:21:45 |
13:21:45 |
13:21:45 @ ./components/icon/assets lazy ^./.*$ namespace object ./tokens/tokenCompletionSuggester.svg
13:21:45 @ ./components/icon/icon.tsx
13:21:45 @ ./components/icon/index.ts
13:21:45 @ ./components/index.ts
13:21:45 @ ./index.ts

@cee-chen
Copy link
Contributor Author

Ahh gotcha, yeah I saw those on build as well but I actually wasn't sure if they'd always been there, and the built version appeared to work. I'll move the .tsx files into a separate folder to test that

- so webpack doesn't get confused when trying to dynamically import an extensionless file that has both a .tsx and .svg folder in the same dir
- also allows us to flatten the tokens/ folder
- was doing a grep for /assets/ and got a little confused by the location of this
- moved faceNeutral (used) to face_neutral (unused) to match other casing
@cee-chen
Copy link
Contributor Author

Not seeing the warnings anymore on build after splitting out the svg and generated tsx files into different folders! 🎉

@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_5212/

@cee-chen
Copy link
Contributor Author

cee-chen commented Sep 30, 2021

... oh wait, I forgot we tell people to access icon/assets directly in our wiki 🤦‍♀️ Kibana start is failing because of that in a security server-side page.

Going to push up a folder name change where I leave the generated files in assets but move the svgs to a new folder called svgs

@cchaos
Copy link
Contributor

cchaos commented Sep 30, 2021

Can you also then update this portion of the wiki https://github.com/elastic/eui/blob/master/wiki/creating-icons.md#prepare-the-pull-request? 😄

@cee-chen
Copy link
Contributor Author

Yeah! I should've just done that here: 5ab0988#diff-54bd9836a1fd4490b5678c576189707530d0b5fc051feb9cecc09c42a3595848

@cee-chen
Copy link
Contributor Author

As of latest commit, EUI's yarn build-pack has no more warnings, Kibana start is working, and my test CRA app is also working 🧘

@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_5212/

@thompsongl
Copy link
Contributor

This is looking good now!
Just want to see if @chandlerprall can think of any problems with removing the file extension from the dynamic import statement 🧐

@chandlerprall
Copy link
Contributor

IIRC, tree-shaking isn't yet working in EUI/webpack so it's not really for perf reasons there, right?

Tree-shaking EUI does work (last I checked at least), but it requires enabling the production environment and not accidentally disabling it in the various ways that one could.

@chandlerprall
Copy link
Contributor

Or alternatively - is there a strong reason to import/load icons dynamically in the first place? Why not import them statically first and then find the right one via name/map later? 🤷

We use the dynamic import to avoid including the icons in the core bundle, as it's unlikely most applications would use them all. Even in Kibana they are not all present on one page, and we see the benefits of not downloading all the svgs at once, which would otherwise add to the initial bundle download & parse times.

@cee-chen
Copy link
Contributor Author

cee-chen commented Oct 4, 2021

Super helpful to get that context - thanks Chandler! I was thankfully able to avoid changing the icon component from using dynamic import in the final solution.

Copy link
Contributor

@chandlerprall chandlerprall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes LGTM! Extensionless import is fine, and I don't believe would have any effects.

@cee-chen cee-chen enabled auto-merge (squash) October 4, 2021 22:11
@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_5212/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants