Skip to content

Commit 8188176

Browse files
authored
Direct SVG rendering (#12157)
Introduce 'make svg' which calls a node script that compiles svg files to `public/img/svg`. These files are vendored to not create a dependency on Node for the backend build. On the frontend side, configure webpack using `raw-loader` so SVGs can be imported as string. Also moved our existing SVGs to web_src/svg for consistency. Fixes: #11618
1 parent 6359101 commit 8188176

File tree

227 files changed

+452
-609
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

227 files changed

+452
-609
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ coverage.all
7979
/public/serviceworker.js
8080
/public/css
8181
/public/fonts
82-
/public/img/svg
8382
/web_src/fomantic/build
8483
/VERSION
8584

Makefile

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,13 @@ FOMANTIC_DEST_DIR := web_src/fomantic/build
9595
WEBPACK_SOURCES := $(shell find web_src/js web_src/less -type f) $(FOMANTIC_DEST)
9696
WEBPACK_CONFIGS := webpack.config.js
9797
WEBPACK_DEST := public/js/index.js public/css/index.css
98-
WEBPACK_DEST_ENTRIES := public/js public/css public/fonts public/serviceworker.js public/img/svg
98+
WEBPACK_DEST_ENTRIES := public/js public/css public/fonts public/serviceworker.js
9999

100100
BINDATA_DEST := modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go
101101
BINDATA_HASH := $(addsuffix .hash,$(BINDATA_DEST))
102102

103+
SVG_DEST_DIR := public/img/svg
104+
103105
TAGS ?=
104106
TAGS_SPLIT := $(subst $(COMMA), ,$(TAGS))
105107
TAGS_EVIDENCE := $(MAKE_EVIDENCE_DIR)/tags
@@ -158,6 +160,7 @@ help:
158160
@echo " - lint-backend lint backend files"
159161
@echo " - watch-frontend watch frontend files and continuously rebuild"
160162
@echo " - webpack build webpack files"
163+
@echo " - svg build svg files"
161164
@echo " - fomantic build fomantic files"
162165
@echo " - generate run \"go generate\""
163166
@echo " - fmt format the Go code"
@@ -292,8 +295,8 @@ lint: lint-backend lint-frontend
292295
lint-backend: golangci-lint revive vet swagger-check swagger-validate test-vendor
293296

294297
.PHONY: lint-frontend
295-
lint-frontend: node_modules
296-
npx eslint web_src/js webpack.config.js
298+
lint-frontend: node_modules svg-check
299+
npx eslint web_src/js build webpack.config.js
297300
npx stylelint web_src/less
298301

299302
.PHONY: watch-frontend
@@ -605,6 +608,20 @@ $(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json | node_
605608
npx webpack --hide-modules --display-entrypoints=false
606609
@touch $(WEBPACK_DEST)
607610

611+
.PHONY: svg
612+
svg: node-check | node_modules
613+
rm -rf $(SVG_DEST_DIR)
614+
node build/generate-svg.js
615+
616+
.PHONY: svg-check
617+
svg-check: svg
618+
@diff=$$(git diff $(SVG_DEST_DIR)); \
619+
if [ -n "$$diff" ]; then \
620+
echo "Please run 'make svg' and commit the result:"; \
621+
echo "$${diff}"; \
622+
exit 1; \
623+
fi;
624+
608625
.PHONY: update-translations
609626
update-translations:
610627
mkdir -p ./translations

build/generate-svg.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#!/usr/bin/env node
2+
'use strict';
3+
4+
const fastGlob = require('fast-glob');
5+
const Svgo = require('svgo');
6+
const {resolve, parse} = require('path');
7+
const {readFile, writeFile, mkdir} = require('fs').promises;
8+
9+
const glob = (pattern) => fastGlob.sync(pattern, {cwd: resolve(__dirname), absolute: true});
10+
const outputDir = resolve(__dirname, '../public/img/svg');
11+
12+
function exit(err) {
13+
if (err) console.error(err);
14+
process.exit(err ? 1 : 0);
15+
}
16+
17+
async function processFile(file, {prefix = ''} = {}) {
18+
const name = `${prefix}${parse(file).name}`;
19+
20+
const svgo = new Svgo({
21+
plugins: [
22+
{removeXMLNS: true},
23+
{removeDimensions: true},
24+
{
25+
addClassesToSVGElement: {
26+
classNames: [
27+
'svg',
28+
name,
29+
],
30+
},
31+
},
32+
{
33+
addAttributesToSVGElement: {
34+
attributes: [
35+
{'width': '16'},
36+
{'height': '16'},
37+
{'aria-hidden': 'true'},
38+
],
39+
},
40+
},
41+
],
42+
});
43+
44+
const {data} = await svgo.optimize(await readFile(file, 'utf8'));
45+
await writeFile(resolve(outputDir, `${name}.svg`), data);
46+
}
47+
48+
async function main() {
49+
try {
50+
await mkdir(outputDir);
51+
} catch {}
52+
53+
for (const file of glob('../node_modules/@primer/octicons/build/svg/*.svg')) {
54+
await processFile(file, {prefix: 'octicon-'});
55+
}
56+
57+
for (const file of glob('../web_src/svg/*.svg')) {
58+
await processFile(file);
59+
}
60+
}
61+
62+
main().then(exit).catch(exit);
63+

docs/content/doc/advanced/hacking-on-gitea.en-us.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ make lint-frontend
151151

152152
Note: When working on frontend code, set `USE_SERVICE_WORKER` to `false` in `app.ini` to prevent undesirable caching of frontend assets.
153153

154+
### Building and adding SVGs
155+
156+
SVG icons are built using the `make svg` target which compiles the icon sources defined in `build/generate-svg.js` into the output directory `public/img/svg`. Custom icons can be added in the `web_src/svg` directory.
157+
154158
### Building Images
155159

156160
To build the images, ImageMagick, `inkscape` and `zopflipng` binaries must be available in

modules/svg/discover_bindata.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2020 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build bindata
6+
7+
package svg
8+
9+
import (
10+
"path"
11+
"path/filepath"
12+
13+
"code.gitea.io/gitea/modules/public"
14+
)
15+
16+
// Discover returns a map of discovered SVG icons in bindata
17+
func Discover() map[string]string {
18+
svgs := make(map[string]string)
19+
20+
for _, file := range public.AssetNames() {
21+
matched, _ := filepath.Match("img/svg/*.svg", file)
22+
if matched {
23+
content, err := public.Asset(file)
24+
if err == nil {
25+
filename := path.Base(file)
26+
svgs[filename[:len(filename)-4]] = string(content)
27+
}
28+
}
29+
}
30+
31+
return svgs
32+
}

modules/svg/discover_nobindata.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2020 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build !bindata
6+
7+
package svg
8+
9+
import (
10+
"io/ioutil"
11+
"path"
12+
"path/filepath"
13+
14+
"code.gitea.io/gitea/modules/setting"
15+
)
16+
17+
// Discover returns a map of discovered SVG icons in the file system
18+
func Discover() map[string]string {
19+
svgs := make(map[string]string)
20+
21+
files, _ := filepath.Glob(path.Join(setting.StaticRootPath, "public", "img", "svg", "*.svg"))
22+
for _, file := range files {
23+
content, err := ioutil.ReadFile(file)
24+
if err == nil {
25+
filename := path.Base(file)
26+
svgs[filename[:len(filename)-4]] = string(content)
27+
}
28+
}
29+
30+
return svgs
31+
}

modules/svg/svg.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2020 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package svg
6+
7+
// SVGs contains discovered SVGs
8+
var SVGs map[string]string
9+
10+
// Init discovers SVGs and populates the `SVGs` variable
11+
func Init() {
12+
SVGs = Discover()
13+
}

modules/templates/helper.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"code.gitea.io/gitea/modules/markup"
3131
"code.gitea.io/gitea/modules/repository"
3232
"code.gitea.io/gitea/modules/setting"
33+
"code.gitea.io/gitea/modules/svg"
3334
"code.gitea.io/gitea/modules/timeutil"
3435
"code.gitea.io/gitea/modules/util"
3536
"code.gitea.io/gitea/services/gitdiff"
@@ -439,9 +440,19 @@ func NewTextFuncMap() []texttmpl.FuncMap {
439440
}}
440441
}
441442

443+
var widthRe = regexp.MustCompile(`width="[0-9]+?"`)
444+
var heightRe = regexp.MustCompile(`height="[0-9]+?"`)
445+
442446
// SVG render icons
443447
func SVG(icon string, size int) template.HTML {
444-
return template.HTML(fmt.Sprintf(`<svg class="svg %s" width="%d" height="%d" aria-hidden="true"><use xlink:href="#%s" /></svg>`, icon, size, size, icon))
448+
if svgStr, ok := svg.SVGs[icon]; ok {
449+
if size != 16 {
450+
svgStr = widthRe.ReplaceAllString(svgStr, fmt.Sprintf(`width="%d"`, size))
451+
svgStr = heightRe.ReplaceAllString(svgStr, fmt.Sprintf(`height="%d"`, size))
452+
}
453+
return template.HTML(svgStr)
454+
}
455+
return template.HTML("")
445456
}
446457

447458
// Safe render raw as HTML

0 commit comments

Comments
 (0)