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
This commit is contained in:
Nolan Lawson 2019-03-02 19:02:06 -08:00 committed by GitHub
parent 2fc6897ee3
commit 880bc7a38a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 255 additions and 176 deletions

View file

@ -11,6 +11,7 @@ tests
/mastodon.log /mastodon.log
/src/template.html /src/template.html
/static/*.css /static/*.css
/static/icons.svg
/static/robots.txt /static/robots.txt
/static/inline-script.js.map /static/inline-script.js.map
/static/emoji-mart-all.json /static/emoji-mart-all.json

1
.gitignore vendored
View file

@ -5,6 +5,7 @@
/mastodon.log /mastodon.log
/src/template.html /src/template.html
/static/*.css /static/*.css
/static/icons.svg
/static/robots.txt /static/robots.txt
/static/inline-script.js.map /static/inline-script.js.map
/static/emoji-mart-all.json /static/emoji-mart-all.json

View file

@ -5,6 +5,7 @@
/mastodon.log /mastodon.log
/src/template.html /src/template.html
/static/*.css /static/*.css
/static/icons.svg
/static/inline-script.js.map /static/inline-script.js.map
/static/emoji-mart-all.json /static/emoji-mart-all.json
/src/inline-script/checksum.js /src/inline-script/checksum.js

View file

@ -7,20 +7,32 @@ import $ from 'cheerio'
const svgo = new SVGO() const svgo = new SVGO()
const readFile = promisify(fs.readFile) 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 = $('<symbol></symbol>')
.attr('id', svg.id)
.attr('viewBox', `0 0 ${optimized.info.width} ${optimized.info.height}`)
.append($path)
return $.xml($symbol)
}
export async function buildSvg () { export async function buildSvg () {
let result = (await Promise.all(svgs.map(async svg => { let inlineSvgs = svgs.filter(_ => _.inline)
let filepath = path.join(__dirname, '../', svg.src) let regularSvgs = svgs.filter(_ => !_.inline)
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 = $('<symbol></symbol>')
.attr('id', svg.id)
.attr('viewBox', `0 0 ${optimized.info.width} ${optimized.info.height}`)
.append($path)
return $.xml($symbol)
}))).join('\n')
return `<svg xmlns="http://www.w3.org/2000/svg" style="display:none;">\n${result}\n</svg>` let inlineSvgStrings = (await Promise.all(inlineSvgs.map(readSvg))).join('')
let regularSvgStrings = (await Promise.all(regularSvgs.map(readSvg))).join('')
let inlineOutput = `<svg xmlns="http://www.w3.org/2000/svg" style="display:none">${inlineSvgStrings}</svg>`
let regularOutput = `<svg xmlns="http://www.w3.org/2000/svg">${regularSvgStrings}</svg>`
await writeFile(path.resolve(__dirname, '../static/icons.svg'), regularOutput, 'utf8')
return inlineOutput
} }

View file

@ -1,9 +1,9 @@
module.exports = [ module.exports = [
{ id: 'pinafore-logo', src: 'src/static/sailboat.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' }, { 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' }, { 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-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', 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-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' }, { 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-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-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-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-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' }, { 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-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-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' }, { id: 'fa-bars', src: 'src/thirdparty/font-awesome-svg-png/white/svg/bars.svg' },

View file

@ -5,10 +5,10 @@
"scripts": { "scripts": {
"lint": "standard && standard --plugin html 'src/routes/**/*.html'", "lint": "standard && standard --plugin html 'src/routes/**/*.html'",
"lint-fix": "standard --fix && standard --fix --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", "serve-dev": "run-p --race build-template-html-watch sapper-dev",
"sapper-dev": "cross-env NODE_ENV=development PORT=4002 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": "cross-env NODE_ENV=production run-s build-steps",
"build-steps": "run-s before-build sapper-export build-now-json", "build-steps": "run-s before-build sapper-export build-now-json",
"sapper-build": "sapper build", "sapper-build": "sapper build",
@ -16,7 +16,7 @@
"build-and-start": "run-s build start", "build-and-start": "run-s build start",
"build-template-html": "node -r esm ./bin/build-template-html.js", "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-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", "run-mastodon": "node -r esm ./bin/run-mastodon.js",
"test": "cross-env BROWSER=chrome:headless run-s test-browser", "test": "cross-env BROWSER=chrome:headless run-s test-browser",
"test-browser": "run-p --race run-mastodon build-and-start test-mastodon", "test-browser": "run-p --race run-mastodon build-and-start test-mastodon",

View file

@ -1,7 +1,5 @@
{#if error} {#if error}
<svg class={computedClass} style={svgStyle} aria-hidden="true"> <SvgIcon className={computedClass} style={svgStyle} href="#fa-user" />
<use xlink:href="#fa-user" />
</svg>
{:elseif $autoplayGifs} {:elseif $autoplayGifs}
<LazyImage <LazyImage
className={computedClass} className={computedClass}
@ -37,7 +35,7 @@
background: none; background: none;
} }
svg.avatar { :global(svg.avatar) {
fill: var(--deemphasized-text-color); fill: var(--deemphasized-text-color);
} }
</style> </style>
@ -46,6 +44,7 @@
import NonAutoplayImg from './NonAutoplayImg.html' import NonAutoplayImg from './NonAutoplayImg.html'
import { classname } from '../_utils/classname' import { classname } from '../_utils/classname'
import LazyImage from './LazyImage.html' import LazyImage from './LazyImage.html'
import SvgIcon from './SvgIcon.html'
export default { export default {
data: () => ({ data: () => ({
@ -80,7 +79,8 @@
}, },
components: { components: {
NonAutoplayImg, NonAutoplayImg,
LazyImage LazyImage,
SvgIcon
} }
} }
</script> </script>

View file

@ -2,9 +2,7 @@
role="navigation" aria-label="Page header" role="navigation" aria-label="Page header"
> >
{#if icon} {#if icon}
<svg class="dynamic-page-banner-svg"> <SvgIcon className="dynamic-page-banner-svg" href={icon} />
<use xlink:href={icon} />
</svg>
{/if} {/if}
<h1 class="dynamic-page-title" aria-label={ariaTitle}>{title}</h1> <h1 class="dynamic-page-title" aria-label={ariaTitle}>{title}</h1>
<button type="button" <button type="button"
@ -24,7 +22,7 @@
.dynamic-page-banner.dynamic-page-with-icon { .dynamic-page-banner.dynamic-page-with-icon {
grid-template-columns: min-content 1fr min-content; grid-template-columns: min-content 1fr min-content;
} }
.dynamic-page-banner-svg { :global(.dynamic-page-banner-svg) {
width: 24px; width: 24px;
height: 24px; height: 24px;
fill: var(--body-text-color); fill: var(--body-text-color);
@ -64,13 +62,17 @@
</style> </style>
<script> <script>
import Shortcut from './shortcut/Shortcut.html' import Shortcut from './shortcut/Shortcut.html'
import SvgIcon from './SvgIcon.html'
export default { export default {
data: () => ({ data: () => ({
icon: void 0, icon: void 0,
ariaTitle: '' ariaTitle: ''
}), }),
components: { Shortcut }, components: {
Shortcut,
SvgIcon
},
methods: { methods: {
onGoBack () { onGoBack () {
window.history.back() window.history.back()

View file

@ -4,27 +4,26 @@
aria-label={ariaLabel} aria-label={ariaLabel}
class={computedClass}> class={computedClass}>
<slot></slot>{#if showIcon} <slot></slot>{#if showIcon}
<svg class="external-link-svg"> <SvgIcon className="external-link-svg" href="#fa-external-link" />
<use xlink:href="#fa-external-link" />
</svg>
{/if}</a> {/if}</a>
<style> <style>
.external-link-with-icon { .external-link-with-icon {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
} }
.external-link-with-icon .external-link-svg { :global(.external-link-with-icon .external-link-svg) {
margin-left: 4px; margin-left: 4px;
width: 14px; width: 14px;
height: 14px; height: 14px;
fill: var(--deemphasized-text-color); fill: var(--deemphasized-text-color);
} }
.external-link-with-icon.normal-icon-color .external-link-svg { :global(.external-link-with-icon.normal-icon-color .external-link-svg) {
fill: var(--body-text-color); fill: var(--body-text-color);
} }
</style> </style>
<script> <script>
import { classname } from '../_utils/classname' import { classname } from '../_utils/classname'
import SvgIcon from './SvgIcon.html'
export default { export default {
data: () => ({ data: () => ({
@ -40,6 +39,9 @@
showIcon && 'external-link-with-icon', showIcon && 'external-link-with-icon',
normalIconColor && 'normal-icon-color' normalIconColor && 'normal-icon-color'
)) ))
},
components: {
SvgIcon
} }
} }
</script> </script>

View file

@ -7,9 +7,7 @@
{disabled} {disabled}
ref:node ref:node
> >
<svg class="icon-button-svg {svgClassName || ''}" ref:svg> <SvgIcon className="icon-button-svg {svgClassName || ''}" ref:svg {href} />
<use xlink:href={href} />
</svg>
</button> </button>
<style> <style>
.icon-button { .icon-button {
@ -21,14 +19,14 @@
justify-content: center; justify-content: center;
} }
.icon-button-svg { :global(.icon-button-svg) {
width: 24px; width: 24px;
height: 24px; height: 24px;
fill: var(--action-button-fill-color); fill: var(--action-button-fill-color);
pointer-events: none; /* hack for Edge */ pointer-events: none; /* hack for Edge */
} }
.icon-button.big-icon .icon-button-svg { :global(.icon-button.big-icon .icon-button-svg) {
width: 32px; width: 32px;
height: 32px; height: 32px;
} }
@ -37,24 +35,24 @@
* regular styles * regular styles
*/ */
.icon-button:hover .icon-button-svg { :global(.icon-button:hover .icon-button-svg) {
fill: var(--action-button-fill-color-hover); fill: var(--action-button-fill-color-hover);
} }
.icon-button.not-pressable:active .icon-button-svg, :global(.icon-button.not-pressable:active .icon-button-svg,
.icon-button.same-pressed:active .icon-button-svg { .icon-button.same-pressed:active .icon-button-svg) {
fill: var(--action-button-fill-color-active); fill: var(--action-button-fill-color-active);
} }
.icon-button.pressed.not-same-pressed .icon-button-svg { :global(.icon-button.pressed.not-same-pressed .icon-button-svg) {
fill: var(--action-button-fill-color-pressed); fill: var(--action-button-fill-color-pressed);
} }
.icon-button.pressed.not-same-pressed:hover .icon-button-svg { :global(.icon-button.pressed.not-same-pressed:hover .icon-button-svg) {
fill: var(--action-button-fill-color-pressed-hover); fill: var(--action-button-fill-color-pressed-hover);
} }
.icon-button.pressed.not-same-pressed:active .icon-button-svg { :global(.icon-button.pressed.not-same-pressed:active .icon-button-svg) {
fill: var(--action-button-fill-color-pressed-active); fill: var(--action-button-fill-color-pressed-active);
} }
@ -62,28 +60,28 @@
* muted * muted
*/ */
.icon-button.muted-style .icon-button-svg { :global(.icon-button.muted-style .icon-button-svg) {
fill: var(--action-button-deemphasized-fill-color); fill: var(--action-button-deemphasized-fill-color);
} }
.icon-button.muted-style:hover .icon-button-svg { :global(.icon-button.muted-style:hover .icon-button-svg) {
fill: var(--action-button-deemphasized-fill-color-hover); fill: var(--action-button-deemphasized-fill-color-hover);
} }
.icon-button.muted-style.not-pressable:active .icon-button-svg, :global(.icon-button.muted-style.not-pressable:active .icon-button-svg,
.icon-button.muted-style.same-pressed:active .icon-button-svg { .icon-button.muted-style.same-pressed:active .icon-button-svg) {
fill: var(--action-button-deemphasized-fill-color-active); fill: var(--action-button-deemphasized-fill-color-active);
} }
.icon-button.muted-style.pressed.not-same-pressed .icon-button-svg { :global(.icon-button.muted-style.pressed.not-same-pressed .icon-button-svg) {
fill: var(--action-button-deemphasized-fill-color-pressed); fill: var(--action-button-deemphasized-fill-color-pressed);
} }
.icon-button.muted-style.pressed.not-same-pressed:hover .icon-button-svg { :global(.icon-button.muted-style.pressed.not-same-pressed:hover .icon-button-svg) {
fill: var(--action-button-deemphasized-fill-color-pressed-hover); fill: var(--action-button-deemphasized-fill-color-pressed-hover);
} }
.icon-button.muted-style.pressed.not-same-pressed:active .icon-button-svg { :global(.icon-button.muted-style.pressed.not-same-pressed:active .icon-button-svg) {
fill: var(--action-button-deemphasized-fill-color-pressed-active); fill: var(--action-button-deemphasized-fill-color-pressed-active);
} }
@ -91,7 +89,7 @@
<script> <script>
import { classname } from '../_utils/classname' import { classname } from '../_utils/classname'
import { store } from '../_store/store' import { store } from '../_store/store'
import { animate } from '../_utils/animate' import SvgIcon from './SvgIcon.html'
export default { export default {
oncreate () { oncreate () {
@ -139,16 +137,14 @@
}, },
methods: { methods: {
animate (animation) { animate (animation) {
let { reduceMotion } = this.store.get() this.refs.svg.animate(animation)
if (!animation || reduceMotion) {
return
}
let svg = this.refs.svg
animate(svg, animation)
}, },
onClick (e) { onClick (e) {
this.fire('click', e) this.fire('click', e)
} }
},
components: {
SvgIcon
} }
} }
</script> </script>

View file

@ -1,23 +1,27 @@
<svg class="loading-spinner-icon spin {maskStyle ? 'mask-style' : ''}" <SvgIcon className="loading-spinner-icon spin {maskStyle ? 'mask-style' : ''}"
style="width: {size}px; height: {size}px;" style="width: {size}px; height: {size}px;"
aria-label="Loading" href="#fa-spinner"
> ariaLabel="Loading"
<use xlink:href="#fa-spinner" /> />
</svg>
<style> <style>
.loading-spinner-icon { :global(.loading-spinner-icon) {
fill: var(--svg-fill); fill: var(--svg-fill);
} }
.loading-spinner-icon.mask-style { :global(.loading-spinner-icon.mask-style) {
fill: var(--mask-svg-fill); fill: var(--mask-svg-fill);
} }
</style> </style>
<script> <script>
import SvgIcon from './SvgIcon.html'
export default { export default {
data: () => ({ data: () => ({
maskStyle: false, maskStyle: false,
size: 64 size: 64
}) }),
components: {
SvgIcon
}
} }
</script> </script>

Before

Width:  |  Height:  |  Size: 443 B

After

Width:  |  Height:  |  Size: 543 B

View file

@ -1,16 +1,12 @@
{#if showBadge} {#if showBadge}
<div class="nav-link-svg-wrapper"> <div class="nav-link-svg-wrapper">
<svg class="nav-link-svg"> <SvgIcon className="nav-link-svg" href={svg} />
<use xlink:href={svg} />
</svg>
<span class="nav-link-badge nav-link-badge-digits-{badgeDigits}" aria-hidden="true"> <span class="nav-link-badge nav-link-badge-digits-{badgeDigits}" aria-hidden="true">
{badgeNumberToShow} {badgeNumberToShow}
</span> </span>
</div> </div>
{:else} {:else}
<svg class="nav-link-svg"> <SvgIcon className="nav-link-svg" href={svg} />
<use xlink:href={svg} />
</svg>
{/if} {/if}
<style> <style>
.nav-link-svg-wrapper { .nav-link-svg-wrapper {
@ -18,7 +14,7 @@
display: inline-block; display: inline-block;
} }
.nav-link-svg-wrapper, .nav-link-svg { :global(.nav-link-svg-wrapper, .nav-link-svg) {
width: 20px; width: 20px;
height: 20px; height: 20px;
} }
@ -51,7 +47,7 @@
font-size: 0.6em; font-size: 0.6em;
} }
.nav-link-svg { :global(.nav-link-svg) {
display: inline-block; display: inline-block;
fill: var(--nav-svg-fill); fill: var(--nav-svg-fill);
} }
@ -61,7 +57,7 @@
} }
@media (max-width: 991px) { @media (max-width: 991px) {
.nav-link-svg-wrapper, .nav-link-svg { :global(.nav-link-svg-wrapper, .nav-link-svg) {
width: 25px; width: 25px;
height: 25px; height: 25px;
} }
@ -72,6 +68,8 @@
} }
</style> </style>
<script> <script>
import SvgIcon from './SvgIcon.html'
export default { export default {
data: () => ({ data: () => ({
showBadge: false, showBadge: false,
@ -80,6 +78,9 @@
computed: { computed: {
badgeDigits: ({ badgeNumber }) => Math.min(3, badgeNumber.toString().length), badgeDigits: ({ badgeNumber }) => Math.min(3, badgeNumber.toString().length),
badgeNumberToShow: ({ badgeNumber }) => (badgeNumber < 100 ? badgeNumber.toString() : '99+') badgeNumberToShow: ({ badgeNumber }) => (badgeNumber < 100 ? badgeNumber.toString() : '99+')
},
components: {
SvgIcon
} }
} }
</script> </script>

View file

@ -2,9 +2,7 @@
<FreeTextLayout> <FreeTextLayout>
<div class="not-logged-in-home"> <div class="not-logged-in-home">
<div class="banner"> <div class="banner">
<svg aria-hidden="true" class="not-logged-in-home-svg"> <SvgIcon className="not-logged-in-home-svg" href="#pinafore-logo" />
<use xlink:href="#pinafore-logo" />
</svg>
<h1>Pinafore</h1> <h1>Pinafore</h1>
</div> </div>
<p>Pinafore is a web client for <ExternalLink href="https://joinmastodon.org">Mastodon</ExternalLink>, designed for speed and simplicity.</p> <p>Pinafore is a web client for <ExternalLink href="https://joinmastodon.org">Mastodon</ExternalLink>, designed for speed and simplicity.</p>
@ -24,7 +22,7 @@
align-items: center; align-items: center;
margin: 0 0 30px; margin: 0 0 30px;
} }
.not-logged-in-home-svg { :global(.not-logged-in-home-svg) {
width: 70px; width: 70px;
height: 70px; height: 70px;
fill: var(--banner-fill); fill: var(--banner-fill);
@ -47,12 +45,14 @@
import FreeTextLayout from './FreeTextLayout.html' import FreeTextLayout from './FreeTextLayout.html'
import HiddenFromSSR from './HiddenFromSSR.html' import HiddenFromSSR from './HiddenFromSSR.html'
import ExternalLink from './ExternalLink.html' import ExternalLink from './ExternalLink.html'
import SvgIcon from './SvgIcon.html'
export default { export default {
components: { components: {
FreeTextLayout, FreeTextLayout,
HiddenFromSSR, HiddenFromSSR,
ExternalLink ExternalLink,
SvgIcon
} }
} }
</script> </script>

View file

@ -1,7 +1,5 @@
<div class="play-video-icon {className || ''}"> <div class="play-video-icon {className || ''}">
<svg class="play-video-icon-svg"> <SvgIcon className="play-video-icon-svg" href="#fa-play-circle" />
<use xlink:href="#fa-play-circle" />
</svg>
</div> </div>
<style> <style>
.play-video-icon { .play-video-icon {
@ -16,7 +14,7 @@
z-index: 40; z-index: 40;
pointer-events: none; pointer-events: none;
} }
.play-video-icon-svg { :global(.play-video-icon-svg) {
width: 72px; width: 72px;
height: 72px; height: 72px;
fill: var(--mask-svg-fill); fill: var(--mask-svg-fill);
@ -25,9 +23,14 @@
} }
</style> </style>
<script> <script>
import SvgIcon from './SvgIcon.html'
export default { export default {
data: () => ({ data: () => ({
className: void 0 className: void 0
}) }),
components: {
SvgIcon
}
} }
</script> </script>

View file

@ -0,0 +1,37 @@
<svg
class={className}
{style}
aria-hidden={!ariaLabel}
aria-label={ariaLabel}
ref:svg>
<use xlink:href="{inline ? '' : '/icons.svg'}{href}" />
</svg>
<script>
import { animate } from '../_utils/animate'
import { store } from '../_store/store'
export default {
data: () => ({
className: '',
style: '',
ariaLabel: ''
}),
store: () => store,
computed: {
inline: ({ href }) => {
// filled in during build
return process.env.INLINE_SVGS.includes(href)
}
},
methods: {
animate (animation) {
let { reduceMotion } = this.store.get()
if (!animation || reduceMotion) {
return
}
let svg = this.refs.svg
animate(svg, animation)
}
}
}
</script>

After

Width:  |  Height:  |  Size: 790 B

View file

@ -1,8 +1,6 @@
<li class="page-list-item"> <li class="page-list-item">
<a {href} rel="prefetch"> <a {href} rel="prefetch">
<svg class="page-list-item-svg"> <SvgIcon className="page-list-item-svg" href={icon} />
<use xlink:href={icon} />
</svg>
<span aria-label={ariaLabel}> <span aria-label={ariaLabel}>
{label} {label}
</span> </span>
@ -40,7 +38,7 @@
.page-list-item a:active { .page-list-item a:active {
background: var(--settings-list-item-bg-active); background: var(--settings-list-item-bg-active);
} }
.page-list-item-svg { :global(.page-list-item-svg) {
width: 24px; width: 24px;
height: 24px; height: 24px;
display: inline-block; display: inline-block;
@ -57,7 +55,7 @@
.page-list-item a { .page-list-item a {
padding: 20px 10px; padding: 20px 10px;
} }
.page-list-item-svg { :global(.page-list-item-svg) {
margin-right: 10px; margin-right: 10px;
} }
} }
@ -66,6 +64,7 @@
<script> <script>
import { store } from '../../_store/store' import { store } from '../../_store/store'
import IconButton from '../IconButton' import IconButton from '../IconButton'
import SvgIcon from '../SvgIcon.html'
export default { export default {
store: () => store, store: () => store,
@ -82,7 +81,8 @@
} }
}, },
components: { components: {
IconButton IconButton,
SvgIcon
}, },
methods: { methods: {
onPinClick (e) { onPinClick (e) {

View file

@ -8,16 +8,14 @@
</span> </span>
<div class="compose-box-button-spinner" <div class="compose-box-button-spinner"
aria-hidden="true"> aria-hidden="true">
<svg class="compose-box-button-svg {$postingStatus ? 'spin' : 'hidden'}"> <SvgIcon className="compose-box-button-svg {$postingStatus ? 'spin' : 'hidden'}"
<use xlink:href="#fa-spinner" /> href="#fa-spinner" />
</svg>
</div> </div>
<div class="compose-box-button-compose {sticky ? '' : 'hidden'}" <div class="compose-box-button-compose {sticky ? '' : 'hidden'}"
aria-hidden="true"> aria-hidden="true">
<svg class="compose-box-button-svg"> <SvgIcon className="compose-box-button-svg"
<use xlink:href="#fa-pencil" /> href="#fa-pencil" />
</svg>
</div> </div>
</button> </button>
</div> </div>
@ -49,7 +47,7 @@
left: 0; left: 0;
right: 0; right: 0;
} }
.compose-box-button-svg { :global(.compose-box-button-svg) {
width: 24px; width: 24px;
height: 24px; height: 24px;
fill: var(--button-primary-text); fill: var(--button-primary-text);
@ -65,12 +63,16 @@
} }
</style> </style>
<script> <script>
import SvgIcon from '../SvgIcon.html'
import { store } from '../../_store/store' import { store } from '../../_store/store'
export default { export default {
store: () => store, store: () => store,
computed: { computed: {
disabled: ({ $postingStatus, overLimit }) => $postingStatus || overLimit disabled: ({ $postingStatus, overLimit }) => $postingStatus || overLimit
},
components: {
SvgIcon
} }
} }
</script> </script>

View file

@ -4,9 +4,7 @@
<button class="compose-media-delete-button" <button class="compose-media-delete-button"
aria-label="Delete {shortName}" aria-label="Delete {shortName}"
on:click="onDeleteMedia()" > on:click="onDeleteMedia()" >
<svg class="compose-media-delete-button-svg"> <SvgIcon className="compose-media-delete-button-svg" href="#fa-times" />
<use xlink:href="#fa-times" />
</svg>
</button> </button>
</div> </div>
<div class="compose-media-alt"> <div class="compose-media-alt">
@ -73,7 +71,7 @@
.compose-media-delete-button:hover { .compose-media-delete-button:hover {
background: var(--toast-border); background: var(--toast-border);
} }
.compose-media-delete-button-svg { :global(.compose-media-delete-button-svg) {
fill: var(--button-text); fill: var(--button-text);
width: 18px; width: 18px;
height: 18px; height: 18px;
@ -95,6 +93,7 @@
import debounce from 'lodash-es/debounce' import debounce from 'lodash-es/debounce'
import { scheduleIdleTask } from '../../_utils/scheduleIdleTask' import { scheduleIdleTask } from '../../_utils/scheduleIdleTask'
import { observe } from 'svelte-extras' import { observe } from 'svelte-extras'
import SvgIcon from '../SvgIcon.html'
export default { export default {
oncreate () { oncreate () {
@ -147,6 +146,9 @@
} = this.get() } = this.get()
deleteMedia(realm, index) deleteMedia(realm, index)
} }
},
components: {
SvgIcon
} }
} }
</script> </script>

View file

@ -2,16 +2,13 @@
{#each items as item (item.key)} {#each items as item (item.key)}
<li class="generic-dialog-list-item"> <li class="generic-dialog-list-item">
<button class="generic-dialog-list-button" on:click="fire('click', item)"> <button class="generic-dialog-list-button" on:click="fire('click', item)">
<svg class="generic-dialog-list-item-svg"> <SvgIcon className="generic-dialog-list-item-svg" href={item.icon} />
<use xlink:href={item.icon} />
</svg>
<span class="generic-dialog-list-button-span"> <span class="generic-dialog-list-button-span">
{item.label} {item.label}
</span> </span>
{#if selectable} {#if selectable}
<svg class="generic-dialog-list-item-svg {item.selected ? '' : 'hidden'}" aria-hidden={!item.selected}> <SvgIcon className="generic-dialog-list-item-svg {item.selected ? '' : 'hidden'}"
<use xlink:href="#fa-check" /> href="#fa-check" />
</svg>
{/if} {/if}
</button> </button>
</li> </li>
@ -31,7 +28,7 @@
font-size: 1.2em; font-size: 1.2em;
display: flex; display: flex;
} }
.generic-dialog-list-item-svg { :global(.generic-dialog-list-item-svg) {
width: 24px; width: 24px;
height: 24px; height: 24px;
fill: var(--svg-fill); fill: var(--svg-fill);
@ -88,3 +85,12 @@
} }
} }
</style> </style>
<script>
import SvgIcon from '../../SvgIcon.html'
export default {
components: {
SvgIcon
}
}
</script>

View file

@ -15,9 +15,7 @@
<div class="close-dialog-button-wrapper"> <div class="close-dialog-button-wrapper">
<button class="close-dialog-button" <button class="close-dialog-button"
data-a11y-dialog-hide aria-label="Close dialog"> data-a11y-dialog-hide aria-label="Close dialog">
<svg class="close-dialog-button-svg"> <SvgIcon className="close-dialog-button-svg" href="#fa-times" />
<use xlink:href="#fa-times" />
</svg>
</button> </button>
</div> </div>
</div> </div>
@ -102,7 +100,7 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
.close-dialog-button-svg { :global(.close-dialog-button-svg) {
padding: 10px; padding: 10px;
fill: var(--button-primary-text); fill: var(--button-primary-text);
width: 24px; width: 24px;
@ -129,7 +127,7 @@
.modal-dialog-title { .modal-dialog-title {
font-size: 1.3em; font-size: 1.3em;
} }
.close-dialog-button-svg { :global(.close-dialog-button-svg) {
padding: 7px; padding: 7px;
width: 18px; width: 18px;
height: 18px; height: 18px;
@ -138,6 +136,7 @@
</style> </style>
<script> <script>
import Shortcut from '../../shortcut/Shortcut.html' import Shortcut from '../../shortcut/Shortcut.html'
import SvgIcon from '../../SvgIcon.html'
import { A11yDialog } from '../../../_thirdparty/a11y-dialog/a11y-dialog' import { A11yDialog } from '../../../_thirdparty/a11y-dialog/a11y-dialog'
import { classname } from '../../../_utils/classname' import { classname } from '../../../_utils/classname'
import { on, emit } from '../../../_utils/eventBus' import { on, emit } from '../../../_utils/eventBus'
@ -164,7 +163,7 @@
ondestroy () { ondestroy () {
popShortcutScope('modal') popShortcutScope('modal')
}, },
components: { Shortcut }, components: { Shortcut, SvgIcon },
data: () => ({ data: () => ({
// don't animate if we're showing a modal dialog on top of another modal dialog. it looks ugly // don't animate if we're showing a modal dialog on top of another modal dialog. it looks ugly
shouldAnimate: !process.browser || document.getElementsByClassName('modal-dialog').length < 2, shouldAnimate: !process.browser || document.getElementsByClassName('modal-dialog').length < 2,

View file

@ -19,9 +19,7 @@
</div> </div>
<div class="account-profile-meta-cell account-profile-meta-verified"> <div class="account-profile-meta-cell account-profile-meta-verified">
{#if field.verified} {#if field.verified}
<svg class="account-profile-meta-verified-svg"> <SvgIcon className="account-profile-meta-verified-svg" href="#fa-check" />
<use xlink:href='#fa-check' />
</svg>
{/if} {/if}
</div> </div>
{/each} {/each}
@ -54,7 +52,7 @@
font-size: 1.1em; font-size: 1.1em;
} }
.account-profile-meta-verified-svg { :global(.account-profile-meta-verified-svg) {
width: 24px; width: 24px;
height: 24px; height: 24px;
fill: var(--svg-fill); fill: var(--svg-fill);
@ -102,6 +100,7 @@
} }
</style> </style>
<script> <script>
import SvgIcon from '../SvgIcon.html'
import { emojifyText } from '../../_utils/emojifyText' import { emojifyText } from '../../_utils/emojifyText'
import { store } from '../../_store/store' import { store } from '../../_store/store'
@ -115,6 +114,9 @@
value: emojifyText(field.value, emojis, $autoplayGifs), value: emojifyText(field.value, emojis, $autoplayGifs),
verified: !!field.verified_at verified: !!field.verified_at
}))) })))
},
components: {
SvgIcon
} }
} }
</script> </script>

View file

@ -1,9 +1,7 @@
<div class="account-profile-moved-banner"> <div class="account-profile-moved-banner">
<Avatar className="from-avatar" size="extra-small" {account} /> <Avatar className="from-avatar" size="extra-small" {account} />
<div class="moved-label"> <div class="moved-label">
<svg class="moved-svg"> <SvgIcon className="moved-svg" href="#fa-suitcase" />
<use xlink:href="#fa-suitcase"/>
</svg>
{accessibleName} has moved: {accessibleName} has moved:
</div> </div>
<a class="moved-avatar" href="/accounts/{moved.id}"> <a class="moved-avatar" href="/accounts/{moved.id}">
@ -27,7 +25,7 @@
grid-area: from-avatar; grid-area: from-avatar;
justify-self: flex-end; justify-self: flex-end;
} }
.moved-svg { :global(.moved-svg) {
width: 18px; width: 18px;
height: 18px; height: 18px;
fill: var(--deemphasized-text-color); fill: var(--deemphasized-text-color);
@ -64,6 +62,7 @@
<script> <script>
import { removeEmoji } from '../../_utils/removeEmoji' import { removeEmoji } from '../../_utils/removeEmoji'
import Avatar from '../Avatar.html' import Avatar from '../Avatar.html'
import SvgIcon from '../SvgIcon.html'
export default { export default {
computed: { computed: {
@ -84,7 +83,8 @@
} }
}, },
components: { components: {
Avatar Avatar,
SvgIcon
} }
} }
</script> </script>

View file

@ -8,9 +8,7 @@
bind:value="$queryInSearch"> bind:value="$queryInSearch">
</div> </div>
<button type="submit" class="primary search-button" aria-label="Search" disabled={$searchLoading}> <button type="submit" class="primary search-button" aria-label="Search" disabled={$searchLoading}>
<svg class="search-button-svg"> <SvgIcon className="search-button-svg" href="#fa-search" />
<use xlink:href="#fa-search" />
</svg>
</button> </button>
</form> </form>
{#if $searchLoading} {#if $searchLoading}
@ -39,7 +37,7 @@
border-radius: 10px; border-radius: 10px;
flex: 1; flex: 1;
} }
.search-button-svg { :global(.search-button-svg) {
fill: var(--button-primary-text); fill: var(--button-primary-text);
width: 18px; width: 18px;
height: 18px; height: 18px;
@ -60,12 +58,14 @@
import LoadingPage from '../LoadingPage.html' import LoadingPage from '../LoadingPage.html'
import { doSearch } from '../../_actions/search' import { doSearch } from '../../_actions/search'
import SearchResults from './SearchResults.html' import SearchResults from './SearchResults.html'
import SvgIcon from '../SvgIcon.html'
export default { export default {
store: () => store, store: () => store,
components: { components: {
LoadingPage, LoadingPage,
SearchResults SearchResults,
SvgIcon
}, },
methods: { methods: {
async onSubmit (e) { async onSubmit (e) {

View file

@ -28,18 +28,14 @@
rel="prefetch" rel="prefetch"
href="/statuses/{originalStatusId}/reblogs" href="/statuses/{originalStatusId}/reblogs"
aria-label={reblogsLabel}> aria-label={reblogsLabel}>
<svg class="status-favs-reblogs-svg"> <SvgIcon className="status-favs-reblogs-svg" href="#fa-retweet" />
<use xlink:href="#fa-retweet"/>
</svg>
<span>{numReblogs}</span> <span>{numReblogs}</span>
</a> </a>
<a class="status-favs-reblogs status-favs" <a class="status-favs-reblogs status-favs"
rel="prefetch" rel="prefetch"
href="/statuses/{originalStatusId}/favorites" href="/statuses/{originalStatusId}/favorites"
aria-label={favoritesLabel}> aria-label={favoritesLabel}>
<svg class="status-favs-reblogs-svg"> <SvgIcon className="status-favs-reblogs-svg" href="#fa-star" />
<use xlink:href="#fa-star" />
</svg>
<span>{numFavs}</span> <span>{numFavs}</span>
</a> </a>
</div> </div>
@ -105,7 +101,7 @@
color: var(--deemphasized-text-color); color: var(--deemphasized-text-color);
} }
.status-favs-reblogs-svg { :global(.status-favs-reblogs-svg) {
fill: var(--deemphasized-text-color); fill: var(--deemphasized-text-color);
width: 18px; width: 18px;
height: 18px; height: 18px;
@ -137,6 +133,7 @@
import ExternalLink from '../ExternalLink.html' import ExternalLink from '../ExternalLink.html'
import { store } from '../../_store/store' import { store } from '../../_store/store'
import { absoluteDateFormatter, shortAbsoluteDateFormatter } from '../../_utils/formatters' import { absoluteDateFormatter, shortAbsoluteDateFormatter } from '../../_utils/formatters'
import SvgIcon from '../SvgIcon.html'
import { on } from '../../_utils/eventBus' import { on } from '../../_utils/eventBus'
export default { export default {
@ -191,7 +188,8 @@
} }
}, },
components: { components: {
ExternalLink ExternalLink,
SvgIcon
} }
} }
</script> </script>

View file

@ -2,9 +2,7 @@
<div class="status-header-avatar {timelineType === 'pinned' ? 'hidden' : ''}"> <div class="status-header-avatar {timelineType === 'pinned' ? 'hidden' : ''}">
<Avatar {account} size="extra-small"/> <Avatar {account} size="extra-small"/>
</div> </div>
<svg class="status-header-svg"> <SvgIcon className="status-header-svg" href={icon} />
<use xlink:href={icon}/>
</svg>
<div class="status-header-content"> <div class="status-header-content">
{#if timelineType === 'pinned'} {#if timelineType === 'pinned'}
@ -50,7 +48,7 @@
margin-left: 19px; /* offset for avatar, 48px - 24px - 5px */ margin-left: 19px; /* offset for avatar, 48px - 24px - 5px */
} }
.status-header-svg { :global(.status-header-svg) {
min-width: 18px; min-width: 18px;
margin-left: 20px; margin-left: 20px;
width: 18px; width: 18px;
@ -58,7 +56,7 @@
fill: var(--deemphasized-text-color); 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); fill: var(--body-text-color);
} }
@ -98,7 +96,7 @@
} }
@media (max-width: 767px) { @media (max-width: 767px) {
.status-header-svg { :global(.status-header-svg) {
margin-left: 10px; margin-left: 10px;
} }
} }
@ -106,11 +104,13 @@
<script> <script>
import Avatar from '../Avatar.html' import Avatar from '../Avatar.html'
import AccountDisplayName from '../profile/AccountDisplayName.html' import AccountDisplayName from '../profile/AccountDisplayName.html'
import SvgIcon from '../SvgIcon.html'
export default { export default {
components: { components: {
Avatar, Avatar,
AccountDisplayName AccountDisplayName,
SvgIcon
}, },
computed: { computed: {
elementId: ({ uuid }) => `status-header-${uuid}`, elementId: ({ uuid }) => `status-header-${uuid}`,

View file

@ -7,9 +7,7 @@
class="status-sensitive-media-button" class="status-sensitive-media-button"
aria-label="Hide sensitive media" > aria-label="Hide sensitive media" >
<div class="svg-wrapper"> <div class="svg-wrapper">
<svg class="status-sensitive-media-svg"> <SvgIcon className="status-sensitive-media-svg" href="#fa-eye-slash" />
<use xlink:href="#fa-eye-slash" />
</svg>
</div> </div>
</button> </button>
<MediaAttachments {mediaAttachments} {sensitive} {uuid} /> <MediaAttachments {mediaAttachments} {sensitive} {uuid} />
@ -23,9 +21,7 @@
Sensitive content. Click to show. Sensitive content. Click to show.
</div> </div>
<div class="svg-wrapper"> <div class="svg-wrapper">
<svg class="status-sensitive-media-svg"> <SvgIcon className="status-sensitive-media-svg" href="#fa-eye" />
<use xlink:href="#fa-eye" />
</svg>
</div> </div>
</button> </button>
{/if} {/if}
@ -135,7 +131,7 @@
.status-sensitive-media-container.status-sensitive-media-shown .svg-wrapper { .status-sensitive-media-container.status-sensitive-media-shown .svg-wrapper {
background: none; background: none;
} }
.status-sensitive-media-svg { :global(.status-sensitive-media-svg) {
width: 24px; width: 24px;
height: 24px; height: 24px;
fill: var(--mask-svg-fill); fill: var(--mask-svg-fill);
@ -144,7 +140,7 @@
margin: 5px; margin: 5px;
padding: 6px 10px; padding: 6px 10px;
} }
.status-sensitive-media-container.status-sensitive-media-hidden .status-sensitive-media-svg { :global(.status-sensitive-media-container.status-sensitive-media-hidden .status-sensitive-media-svg) {
fill: var(--deemphasized-text-color); fill: var(--deemphasized-text-color);
background: var(--mask-opaque-bg); background: var(--mask-opaque-bg);
} }
@ -152,6 +148,7 @@
<script> <script>
import MediaAttachments from './MediaAttachments.html' import MediaAttachments from './MediaAttachments.html'
import Shortcut from '../shortcut/Shortcut.html' import Shortcut from '../shortcut/Shortcut.html'
import SvgIcon from '../SvgIcon.html'
import { store } from '../../_store/store' import { store } from '../../_store/store'
import { registerClickDelegate } from '../../_utils/delegate' import { registerClickDelegate } from '../../_utils/delegate'
import { classname } from '../../_utils/classname' import { classname } from '../../_utils/classname'
@ -163,7 +160,8 @@
}, },
components: { components: {
MediaAttachments, MediaAttachments,
Shortcut Shortcut,
SvgIcon
}, },
store: () => store, store: () => store,
computed: { computed: {

View file

@ -16,9 +16,8 @@
title={instance.switchLabel} title={instance.switchLabel}
aria-pressed={instance.current} aria-pressed={instance.current}
on:click="onSwitchToThisInstance(event, instance.name)"> on:click="onSwitchToThisInstance(event, instance.name)">
<svg class="instance-switcher-button-svg"> <SvgIcon className="instance-switcher-button-svg"
<use xlink:href="{instance.current ? '#fa-star' : '#fa-star-o'}" /> href={instance.current ? '#fa-star' : '#fa-star-o'} />
</svg>
</button> </button>
</div> </div>
</SettingsListRow> </SettingsListRow>
@ -55,7 +54,7 @@
justify-content: center; justify-content: center;
margin: 1px; margin: 1px;
} }
.instance-switcher-button-svg { :global(.instance-switcher-button-svg) {
width: 24px; width: 24px;
height: 24px; height: 24px;
display: inline-block; display: inline-block;
@ -69,13 +68,15 @@
import SettingsList from '../../../_components/settings/SettingsList.html' import SettingsList from '../../../_components/settings/SettingsList.html'
import SettingsListRow from '../../../_components/settings/SettingsListRow.html' import SettingsListRow from '../../../_components/settings/SettingsListRow.html'
import SettingsListButton from '../../../_components/settings/SettingsListButton.html' import SettingsListButton from '../../../_components/settings/SettingsListButton.html'
import SvgIcon from '../../../_components/SvgIcon.html'
export default { export default {
components: { components: {
SettingsLayout, SettingsLayout,
SettingsList, SettingsList,
SettingsListRow, SettingsListRow,
SettingsListButton SettingsListButton,
SvgIcon
}, },
methods: { methods: {
onSwitchToThisInstance (e, instanceName) { onSwitchToThisInstance (e, instanceName) {

View file

@ -4,7 +4,7 @@ const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPl
const LodashModuleReplacementPlugin = require('lodash-webpack-plugin') const LodashModuleReplacementPlugin = require('lodash-webpack-plugin')
const terser = require('./terser.config') const terser = require('./terser.config')
const CircularDependencyPlugin = require('circular-dependency-plugin') const CircularDependencyPlugin = require('circular-dependency-plugin')
const { mode, dev, resolve } = require('./shared.config') const { mode, dev, resolve, inlineSvgs } = require('./shared.config')
const output = Object.assign(config.client.output(), { const output = Object.assign(config.client.output(), {
// enables HMR in workers // enables HMR in workers
@ -51,6 +51,11 @@ module.exports = {
} }
}, },
plugins: [ plugins: [
new webpack.DefinePlugin({
'process.browser': true,
'process.env.NODE_ENV': JSON.stringify(mode),
'process.env.INLINE_SVGS': JSON.stringify(inlineSvgs)
}),
new webpack.NormalModuleReplacementPlugin( new webpack.NormalModuleReplacementPlugin(
/\/_database\/database\.js$/, // this version plays nicer with IDEs /\/_database\/database\.js$/, // this version plays nicer with IDEs
'./database.prod.js' './database.prod.js'
@ -66,10 +71,6 @@ module.exports = {
requestTimeout: 120000 requestTimeout: 120000
}) })
] : [ ] : [
new webpack.DefinePlugin({
'process.browser': true,
'process.env.NODE_ENV': JSON.stringify(mode)
}),
new BundleAnalyzerPlugin({ // generates report.html and stats.json new BundleAnalyzerPlugin({ // generates report.html and stats.json
analyzerMode: 'static', analyzerMode: 'static',
generateStatsFile: true, generateStatsFile: true,

View file

@ -1,6 +1,7 @@
const webpack = require('webpack')
const config = require('sapper/config/webpack.js') const config = require('sapper/config/webpack.js')
const pkg = require('../package.json') const pkg = require('../package.json')
const { mode, dev, resolve } = require('./shared.config') const { mode, dev, resolve, inlineSvgs } = require('./shared.config')
module.exports = { module.exports = {
entry: config.server.entry(), entry: config.server.entry(),
@ -28,5 +29,10 @@ module.exports = {
mode, mode,
performance: { performance: {
hints: false // it doesn't matter if server.js is large hints: false // it doesn't matter if server.js is large
} },
plugins: [
new webpack.DefinePlugin({
'process.env.INLINE_SVGS': JSON.stringify(inlineSvgs)
})
]
} }

View file

@ -1,4 +1,7 @@
const mode = process.env.NODE_ENV const svgs = require('../bin/svgs')
const inlineSvgs = svgs.filter(_ => _.inline).map(_ => `#${_.id}`)
const mode = process.env.NODE_ENV || 'production'
const dev = mode === 'development' const dev = mode === 'development'
const resolve = { const resolve = {
@ -14,5 +17,6 @@ const resolve = {
module.exports = { module.exports = {
mode, mode,
dev, dev,
resolve resolve,
inlineSvgs
} }