Skip to content

Commit

Permalink
Direct SVG rendering (go-gitea#12157)
Browse files Browse the repository at this point in the history
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: go-gitea#11618
  • Loading branch information
silverwind authored Jul 12, 2020
1 parent 6359101 commit 8188176
Show file tree
Hide file tree
Showing 227 changed files with 452 additions and 609 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ coverage.all
/public/serviceworker.js
/public/css
/public/fonts
/public/img/svg
/web_src/fomantic/build
/VERSION

Expand Down
23 changes: 20 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,13 @@ FOMANTIC_DEST_DIR := web_src/fomantic/build
WEBPACK_SOURCES := $(shell find web_src/js web_src/less -type f) $(FOMANTIC_DEST)
WEBPACK_CONFIGS := webpack.config.js
WEBPACK_DEST := public/js/index.js public/css/index.css
WEBPACK_DEST_ENTRIES := public/js public/css public/fonts public/serviceworker.js public/img/svg
WEBPACK_DEST_ENTRIES := public/js public/css public/fonts public/serviceworker.js

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

SVG_DEST_DIR := public/img/svg

TAGS ?=
TAGS_SPLIT := $(subst $(COMMA), ,$(TAGS))
TAGS_EVIDENCE := $(MAKE_EVIDENCE_DIR)/tags
Expand Down Expand Up @@ -158,6 +160,7 @@ help:
@echo " - lint-backend lint backend files"
@echo " - watch-frontend watch frontend files and continuously rebuild"
@echo " - webpack build webpack files"
@echo " - svg build svg files"
@echo " - fomantic build fomantic files"
@echo " - generate run \"go generate\""
@echo " - fmt format the Go code"
Expand Down Expand Up @@ -292,8 +295,8 @@ lint: lint-backend lint-frontend
lint-backend: golangci-lint revive vet swagger-check swagger-validate test-vendor

.PHONY: lint-frontend
lint-frontend: node_modules
npx eslint web_src/js webpack.config.js
lint-frontend: node_modules svg-check
npx eslint web_src/js build webpack.config.js
npx stylelint web_src/less

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

.PHONY: svg
svg: node-check | node_modules
rm -rf $(SVG_DEST_DIR)
node build/generate-svg.js

.PHONY: svg-check
svg-check: svg
@diff=$$(git diff $(SVG_DEST_DIR)); \
if [ -n "$$diff" ]; then \
echo "Please run 'make svg' and commit the result:"; \
echo "$${diff}"; \
exit 1; \
fi;

.PHONY: update-translations
update-translations:
mkdir -p ./translations
Expand Down
63 changes: 63 additions & 0 deletions build/generate-svg.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env node
'use strict';

const fastGlob = require('fast-glob');
const Svgo = require('svgo');
const {resolve, parse} = require('path');
const {readFile, writeFile, mkdir} = require('fs').promises;

const glob = (pattern) => fastGlob.sync(pattern, {cwd: resolve(__dirname), absolute: true});
const outputDir = resolve(__dirname, '../public/img/svg');

function exit(err) {
if (err) console.error(err);
process.exit(err ? 1 : 0);
}

async function processFile(file, {prefix = ''} = {}) {
const name = `${prefix}${parse(file).name}`;

const svgo = new Svgo({
plugins: [
{removeXMLNS: true},
{removeDimensions: true},
{
addClassesToSVGElement: {
classNames: [
'svg',
name,
],
},
},
{
addAttributesToSVGElement: {
attributes: [
{'width': '16'},
{'height': '16'},
{'aria-hidden': 'true'},
],
},
},
],
});

const {data} = await svgo.optimize(await readFile(file, 'utf8'));
await writeFile(resolve(outputDir, `${name}.svg`), data);
}

async function main() {
try {
await mkdir(outputDir);
} catch {}

for (const file of glob('../node_modules/@primer/octicons/build/svg/*.svg')) {
await processFile(file, {prefix: 'octicon-'});
}

for (const file of glob('../web_src/svg/*.svg')) {
await processFile(file);
}
}

main().then(exit).catch(exit);

4 changes: 4 additions & 0 deletions docs/content/doc/advanced/hacking-on-gitea.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@ make lint-frontend

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

### Building and adding SVGs

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.

### Building Images

To build the images, ImageMagick, `inkscape` and `zopflipng` binaries must be available in
Expand Down
32 changes: 32 additions & 0 deletions modules/svg/discover_bindata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

// +build bindata

package svg

import (
"path"
"path/filepath"

"code.gitea.io/gitea/modules/public"
)

// Discover returns a map of discovered SVG icons in bindata
func Discover() map[string]string {
svgs := make(map[string]string)

for _, file := range public.AssetNames() {
matched, _ := filepath.Match("img/svg/*.svg", file)
if matched {
content, err := public.Asset(file)
if err == nil {
filename := path.Base(file)
svgs[filename[:len(filename)-4]] = string(content)
}
}
}

return svgs
}
31 changes: 31 additions & 0 deletions modules/svg/discover_nobindata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

// +build !bindata

package svg

import (
"io/ioutil"
"path"
"path/filepath"

"code.gitea.io/gitea/modules/setting"
)

// Discover returns a map of discovered SVG icons in the file system
func Discover() map[string]string {
svgs := make(map[string]string)

files, _ := filepath.Glob(path.Join(setting.StaticRootPath, "public", "img", "svg", "*.svg"))
for _, file := range files {
content, err := ioutil.ReadFile(file)
if err == nil {
filename := path.Base(file)
svgs[filename[:len(filename)-4]] = string(content)
}
}

return svgs
}
13 changes: 13 additions & 0 deletions modules/svg/svg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package svg

// SVGs contains discovered SVGs
var SVGs map[string]string

// Init discovers SVGs and populates the `SVGs` variable
func Init() {
SVGs = Discover()
}
13 changes: 12 additions & 1 deletion modules/templates/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/svg"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/gitdiff"
Expand Down Expand Up @@ -439,9 +440,19 @@ func NewTextFuncMap() []texttmpl.FuncMap {
}}
}

var widthRe = regexp.MustCompile(`width="[0-9]+?"`)
var heightRe = regexp.MustCompile(`height="[0-9]+?"`)

// SVG render icons
func SVG(icon string, size int) template.HTML {
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))
if svgStr, ok := svg.SVGs[icon]; ok {
if size != 16 {
svgStr = widthRe.ReplaceAllString(svgStr, fmt.Sprintf(`width="%d"`, size))
svgStr = heightRe.ReplaceAllString(svgStr, fmt.Sprintf(`height="%d"`, size))
}
return template.HTML(svgStr)
}
return template.HTML("")
}

// Safe render raw as HTML
Expand Down
Loading

0 comments on commit 8188176

Please sign in to comment.