From 880bc7a38af8b8ab4d63a8713b83c88ae8bfa177 Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Sat, 2 Mar 2019 19:02:06 -0800 Subject: [PATCH] perf: use a separate icons.svg file (#1067) * perf: use a separate icons.svg file This splits icons into inline and non-inline. The inline ones are high priority; the rest go in an icons.svg file. * create SvgIcon.html * determine inlined svgs at build time --- .dockerignore | 1 + .gitignore | 1 + .nowignore | 1 + ...-third-party-assets.js => build-assets.js} | 0 bin/build-svg.js | 38 +++++++++------ bin/svgs.js | 12 ++--- package.json | 6 +-- src/routes/_components/Avatar.html | 10 ++-- src/routes/_components/DynamicPageBanner.html | 12 +++-- src/routes/_components/ExternalLink.html | 14 +++--- src/routes/_components/IconButton.html | 46 +++++++++---------- src/routes/_components/LoadingSpinner.html | 24 ++++++---- src/routes/_components/NavItemIcon.html | 19 ++++---- src/routes/_components/NotLoggedInHome.html | 10 ++-- src/routes/_components/PlayVideoIcon.html | 15 +++--- src/routes/_components/SvgIcon.html | 37 +++++++++++++++ .../_components/community/PageListItem.html | 12 ++--- .../_components/compose/ComposeButton.html | 16 ++++--- .../_components/compose/ComposeMediaItem.html | 10 ++-- .../dialog/components/GenericDialogList.html | 20 +++++--- .../dialog/components/ModalDialog.html | 11 ++--- .../profile/AccountProfileMeta.html | 10 ++-- .../profile/AccountProfileMovedBanner.html | 10 ++-- src/routes/_components/search/Search.html | 12 ++--- .../_components/status/StatusDetails.html | 14 +++--- .../_components/status/StatusHeader.html | 14 +++--- .../status/StatusMediaAttachments.html | 16 +++---- .../_pages/settings/instances/index.html | 11 +++-- webpack/client.config.js | 11 +++-- webpack/server.config.js | 10 +++- webpack/shared.config.js | 8 +++- 31 files changed, 255 insertions(+), 176 deletions(-) rename bin/{build-third-party-assets.js => build-assets.js} (100%) create mode 100644 src/routes/_components/SvgIcon.html diff --git a/.dockerignore b/.dockerignore index 78e905a2..2546f8a4 100644 --- a/.dockerignore +++ b/.dockerignore @@ -11,6 +11,7 @@ tests /mastodon.log /src/template.html /static/*.css +/static/icons.svg /static/robots.txt /static/inline-script.js.map /static/emoji-mart-all.json diff --git a/.gitignore b/.gitignore index 06a287ee..226aad4a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ /mastodon.log /src/template.html /static/*.css +/static/icons.svg /static/robots.txt /static/inline-script.js.map /static/emoji-mart-all.json diff --git a/.nowignore b/.nowignore index d09ba4c3..4993a364 100644 --- a/.nowignore +++ b/.nowignore @@ -5,6 +5,7 @@ /mastodon.log /src/template.html /static/*.css +/static/icons.svg /static/inline-script.js.map /static/emoji-mart-all.json /src/inline-script/checksum.js diff --git a/bin/build-third-party-assets.js b/bin/build-assets.js similarity index 100% rename from bin/build-third-party-assets.js rename to bin/build-assets.js diff --git a/bin/build-svg.js b/bin/build-svg.js index 1da2a9ca..ef185c2e 100755 --- a/bin/build-svg.js +++ b/bin/build-svg.js @@ -7,20 +7,32 @@ import $ from 'cheerio' const svgo = new SVGO() const readFile = promisify(fs.readFile) +const writeFile = promisify(fs.writeFile) + +async function readSvg (svg) { + let filepath = path.join(__dirname, '../', svg.src) + let content = await readFile(filepath, 'utf8') + let optimized = (await svgo.optimize(content)) + let $optimized = $(optimized.data) + let $path = $optimized.find('path').removeAttr('fill') + let $symbol = $('') + .attr('id', svg.id) + .attr('viewBox', `0 0 ${optimized.info.width} ${optimized.info.height}`) + .append($path) + return $.xml($symbol) +} export async function buildSvg () { - let result = (await Promise.all(svgs.map(async svg => { - let filepath = path.join(__dirname, '../', svg.src) - let content = await readFile(filepath, 'utf8') - let optimized = (await svgo.optimize(content)) - let $optimized = $(optimized.data) - let $path = $optimized.find('path').removeAttr('fill') - let $symbol = $('') - .attr('id', svg.id) - .attr('viewBox', `0 0 ${optimized.info.width} ${optimized.info.height}`) - .append($path) - return $.xml($symbol) - }))).join('\n') + let inlineSvgs = svgs.filter(_ => _.inline) + let regularSvgs = svgs.filter(_ => !_.inline) - return `\n${result}\n` + let inlineSvgStrings = (await Promise.all(inlineSvgs.map(readSvg))).join('') + let regularSvgStrings = (await Promise.all(regularSvgs.map(readSvg))).join('') + + let inlineOutput = `${inlineSvgStrings}` + let regularOutput = `${regularSvgStrings}` + + await writeFile(path.resolve(__dirname, '../static/icons.svg'), regularOutput, 'utf8') + + return inlineOutput } diff --git a/bin/svgs.js b/bin/svgs.js index 966b0c06..4f322b6b 100644 --- a/bin/svgs.js +++ b/bin/svgs.js @@ -1,9 +1,9 @@ module.exports = [ - { id: 'pinafore-logo', src: 'src/static/sailboat.svg' }, - { id: 'fa-bell', src: 'src/thirdparty/font-awesome-svg-png/white/svg/bell.svg' }, - { id: 'fa-users', src: 'src/thirdparty/font-awesome-svg-png/white/svg/users.svg' }, + { id: 'pinafore-logo', src: 'src/static/sailboat.svg', inline: true }, + { id: 'fa-bell', src: 'src/thirdparty/font-awesome-svg-png/white/svg/bell.svg', inline: true }, + { id: 'fa-users', src: 'src/thirdparty/font-awesome-svg-png/white/svg/users.svg', inline: true }, { id: 'fa-globe', src: 'src/thirdparty/font-awesome-svg-png/white/svg/globe.svg' }, - { id: 'fa-gear', src: 'src/thirdparty/font-awesome-svg-png/white/svg/gear.svg' }, + { id: 'fa-gear', src: 'src/thirdparty/font-awesome-svg-png/white/svg/gear.svg', inline: true }, { id: 'fa-reply', src: 'src/thirdparty/font-awesome-svg-png/white/svg/reply.svg' }, { id: 'fa-reply-all', src: 'src/thirdparty/font-awesome-svg-png/white/svg/reply-all.svg' }, { id: 'fa-retweet', src: 'src/thirdparty/font-awesome-svg-png/white/svg/retweet.svg' }, @@ -21,8 +21,8 @@ module.exports = [ { id: 'fa-user-times', src: 'src/thirdparty/font-awesome-svg-png/white/svg/user-times.svg' }, { id: 'fa-user-plus', src: 'src/thirdparty/font-awesome-svg-png/white/svg/user-plus.svg' }, { id: 'fa-external-link', src: 'src/thirdparty/font-awesome-svg-png/white/svg/external-link.svg' }, - { id: 'fa-search', src: 'src/thirdparty/font-awesome-svg-png/white/svg/search.svg' }, - { id: 'fa-comments', src: 'src/thirdparty/font-awesome-svg-png/white/svg/comments.svg' }, + { id: 'fa-search', src: 'src/thirdparty/font-awesome-svg-png/white/svg/search.svg', inline: true }, + { id: 'fa-comments', src: 'src/thirdparty/font-awesome-svg-png/white/svg/comments.svg', inline: true }, { id: 'fa-paperclip', src: 'src/thirdparty/font-awesome-svg-png/white/svg/paperclip.svg' }, { id: 'fa-thumb-tack', src: 'src/thirdparty/font-awesome-svg-png/white/svg/thumb-tack.svg' }, { id: 'fa-bars', src: 'src/thirdparty/font-awesome-svg-png/white/svg/bars.svg' }, diff --git a/package.json b/package.json index 24f25568..ad21672e 100644 --- a/package.json +++ b/package.json @@ -5,10 +5,10 @@ "scripts": { "lint": "standard && standard --plugin html 'src/routes/**/*.html'", "lint-fix": "standard --fix && standard --fix --plugin html 'src/routes/**/*.html'", - "dev": "run-s build-template-html build-third-party-assets serve-dev", + "dev": "run-s build-template-html build-assets serve-dev", "serve-dev": "run-p --race build-template-html-watch sapper-dev", "sapper-dev": "cross-env NODE_ENV=development PORT=4002 sapper dev", - "before-build": "run-s build-template-html build-third-party-assets", + "before-build": "run-s build-template-html build-assets", "build": "cross-env NODE_ENV=production run-s build-steps", "build-steps": "run-s before-build sapper-export build-now-json", "sapper-build": "sapper build", @@ -16,7 +16,7 @@ "build-and-start": "run-s build start", "build-template-html": "node -r esm ./bin/build-template-html.js", "build-template-html-watch": "node -r esm ./bin/build-template-html.js --watch", - "build-third-party-assets": "node -r esm ./bin/build-third-party-assets.js", + "build-assets": "node -r esm ./bin/build-assets.js", "run-mastodon": "node -r esm ./bin/run-mastodon.js", "test": "cross-env BROWSER=chrome:headless run-s test-browser", "test-browser": "run-p --race run-mastodon build-and-start test-mastodon", diff --git a/src/routes/_components/Avatar.html b/src/routes/_components/Avatar.html index f8d07998..6e68562e 100644 --- a/src/routes/_components/Avatar.html +++ b/src/routes/_components/Avatar.html @@ -1,7 +1,5 @@ {#if error} - + {:elseif $autoplayGifs} @@ -46,6 +44,7 @@ import NonAutoplayImg from './NonAutoplayImg.html' import { classname } from '../_utils/classname' import LazyImage from './LazyImage.html' + import SvgIcon from './SvgIcon.html' export default { data: () => ({ @@ -80,7 +79,8 @@ }, components: { NonAutoplayImg, - LazyImage + LazyImage, + SvgIcon } } diff --git a/src/routes/_components/DynamicPageBanner.html b/src/routes/_components/DynamicPageBanner.html index 4ab961bb..d9575674 100644 --- a/src/routes/_components/DynamicPageBanner.html +++ b/src/routes/_components/DynamicPageBanner.html @@ -2,9 +2,7 @@ role="navigation" aria-label="Page header" > {#if icon} - - - + {/if}

{title}

\ No newline at end of file + diff --git a/src/routes/_components/NavItemIcon.html b/src/routes/_components/NavItemIcon.html index 858866d9..565f3045 100644 --- a/src/routes/_components/NavItemIcon.html +++ b/src/routes/_components/NavItemIcon.html @@ -1,16 +1,12 @@ {#if showBadge} {:else} - - - + {/if} diff --git a/src/routes/_components/NotLoggedInHome.html b/src/routes/_components/NotLoggedInHome.html index 2322b7d8..c932b0cc 100644 --- a/src/routes/_components/NotLoggedInHome.html +++ b/src/routes/_components/NotLoggedInHome.html @@ -2,9 +2,7 @@

Pinafore is a web client for Mastodon, designed for speed and simplicity.

@@ -24,7 +22,7 @@ align-items: center; margin: 0 0 30px; } - .not-logged-in-home-svg { + :global(.not-logged-in-home-svg) { width: 70px; height: 70px; fill: var(--banner-fill); @@ -47,12 +45,14 @@ import FreeTextLayout from './FreeTextLayout.html' import HiddenFromSSR from './HiddenFromSSR.html' import ExternalLink from './ExternalLink.html' + import SvgIcon from './SvgIcon.html' export default { components: { FreeTextLayout, HiddenFromSSR, - ExternalLink + ExternalLink, + SvgIcon } } diff --git a/src/routes/_components/PlayVideoIcon.html b/src/routes/_components/PlayVideoIcon.html index f2f0a077..1534e6c6 100644 --- a/src/routes/_components/PlayVideoIcon.html +++ b/src/routes/_components/PlayVideoIcon.html @@ -1,7 +1,5 @@
- - - +
\ No newline at end of file + diff --git a/src/routes/_components/SvgIcon.html b/src/routes/_components/SvgIcon.html new file mode 100644 index 00000000..f15126ed --- /dev/null +++ b/src/routes/_components/SvgIcon.html @@ -0,0 +1,37 @@ + + + + diff --git a/src/routes/_components/community/PageListItem.html b/src/routes/_components/community/PageListItem.html index df733fb9..8b406b73 100644 --- a/src/routes/_components/community/PageListItem.html +++ b/src/routes/_components/community/PageListItem.html @@ -1,8 +1,6 @@
  • - - - + {label} @@ -40,7 +38,7 @@ .page-list-item a:active { background: var(--settings-list-item-bg-active); } - .page-list-item-svg { + :global(.page-list-item-svg) { width: 24px; height: 24px; display: inline-block; @@ -57,7 +55,7 @@ .page-list-item a { padding: 20px 10px; } - .page-list-item-svg { + :global(.page-list-item-svg) { margin-right: 10px; } } @@ -66,6 +64,7 @@ diff --git a/src/routes/_components/compose/ComposeMediaItem.html b/src/routes/_components/compose/ComposeMediaItem.html index 98b18bc6..21003806 100644 --- a/src/routes/_components/compose/ComposeMediaItem.html +++ b/src/routes/_components/compose/ComposeMediaItem.html @@ -4,9 +4,7 @@
  • @@ -73,7 +71,7 @@ .compose-media-delete-button:hover { background: var(--toast-border); } - .compose-media-delete-button-svg { + :global(.compose-media-delete-button-svg) { fill: var(--button-text); width: 18px; height: 18px; @@ -95,6 +93,7 @@ import debounce from 'lodash-es/debounce' import { scheduleIdleTask } from '../../_utils/scheduleIdleTask' import { observe } from 'svelte-extras' + import SvgIcon from '../SvgIcon.html' export default { oncreate () { @@ -147,6 +146,9 @@ } = this.get() deleteMedia(realm, index) } + }, + components: { + SvgIcon } } diff --git a/src/routes/_components/dialog/components/GenericDialogList.html b/src/routes/_components/dialog/components/GenericDialogList.html index 8d50167c..b68d6ae9 100644 --- a/src/routes/_components/dialog/components/GenericDialogList.html +++ b/src/routes/_components/dialog/components/GenericDialogList.html @@ -2,16 +2,13 @@ {#each items as item (item.key)}
  • @@ -31,7 +28,7 @@ font-size: 1.2em; display: flex; } - .generic-dialog-list-item-svg { + :global(.generic-dialog-list-item-svg) { width: 24px; height: 24px; fill: var(--svg-fill); @@ -88,3 +85,12 @@ } } + diff --git a/src/routes/_components/dialog/components/ModalDialog.html b/src/routes/_components/dialog/components/ModalDialog.html index a8e79ceb..1248a3f3 100644 --- a/src/routes/_components/dialog/components/ModalDialog.html +++ b/src/routes/_components/dialog/components/ModalDialog.html @@ -15,9 +15,7 @@
    @@ -102,7 +100,7 @@ justify-content: center; align-items: center; } - .close-dialog-button-svg { + :global(.close-dialog-button-svg) { padding: 10px; fill: var(--button-primary-text); width: 24px; @@ -129,7 +127,7 @@ .modal-dialog-title { font-size: 1.3em; } - .close-dialog-button-svg { + :global(.close-dialog-button-svg) { padding: 7px; width: 18px; height: 18px; @@ -138,6 +136,7 @@ diff --git a/src/routes/_components/profile/AccountProfileMovedBanner.html b/src/routes/_components/profile/AccountProfileMovedBanner.html index 099a9b53..708a1568 100644 --- a/src/routes/_components/profile/AccountProfileMovedBanner.html +++ b/src/routes/_components/profile/AccountProfileMovedBanner.html @@ -1,9 +1,7 @@
    {#if $searchLoading} @@ -39,7 +37,7 @@ border-radius: 10px; flex: 1; } - .search-button-svg { + :global(.search-button-svg) { fill: var(--button-primary-text); width: 18px; height: 18px; @@ -60,12 +58,14 @@ import LoadingPage from '../LoadingPage.html' import { doSearch } from '../../_actions/search' import SearchResults from './SearchResults.html' + import SvgIcon from '../SvgIcon.html' export default { store: () => store, components: { LoadingPage, - SearchResults + SearchResults, + SvgIcon }, methods: { async onSubmit (e) { @@ -74,4 +74,4 @@ } } } - \ No newline at end of file + diff --git a/src/routes/_components/status/StatusDetails.html b/src/routes/_components/status/StatusDetails.html index f8a857d1..cc9cf953 100644 --- a/src/routes/_components/status/StatusDetails.html +++ b/src/routes/_components/status/StatusDetails.html @@ -28,18 +28,14 @@ rel="prefetch" href="/statuses/{originalStatusId}/reblogs" aria-label={reblogsLabel}> - - - + {numReblogs} - - - + {numFavs} @@ -105,7 +101,7 @@ color: var(--deemphasized-text-color); } - .status-favs-reblogs-svg { + :global(.status-favs-reblogs-svg) { fill: var(--deemphasized-text-color); width: 18px; height: 18px; @@ -137,6 +133,7 @@ import ExternalLink from '../ExternalLink.html' import { store } from '../../_store/store' import { absoluteDateFormatter, shortAbsoluteDateFormatter } from '../../_utils/formatters' + import SvgIcon from '../SvgIcon.html' import { on } from '../../_utils/eventBus' export default { @@ -191,7 +188,8 @@ } }, components: { - ExternalLink + ExternalLink, + SvgIcon } } diff --git a/src/routes/_components/status/StatusHeader.html b/src/routes/_components/status/StatusHeader.html index 226d700d..fabea6e1 100644 --- a/src/routes/_components/status/StatusHeader.html +++ b/src/routes/_components/status/StatusHeader.html @@ -2,9 +2,7 @@
    - - - +
    {#if timelineType === 'pinned'} @@ -50,7 +48,7 @@ margin-left: 19px; /* offset for avatar, 48px - 24px - 5px */ } - .status-header-svg { + :global(.status-header-svg) { min-width: 18px; margin-left: 20px; width: 18px; @@ -58,7 +56,7 @@ fill: var(--deemphasized-text-color); } - .status-header.status-in-notification .status-header-svg { + :global(.status-header.status-in-notification .status-header-svg) { fill: var(--body-text-color); } @@ -98,7 +96,7 @@ } @media (max-width: 767px) { - .status-header-svg { + :global(.status-header-svg) { margin-left: 10px; } } @@ -106,11 +104,13 @@