1
1
mirror of https://github.com/go-gitea/gitea synced 2025-09-10 02:38:28 +00:00

Switch to @resvg/resvg-wasm for generate-images (#35415)

Use the WASM module of [`resvg-js`](https://github.com/thx/resvg-js) to
replace `fabric` and the problematic native `canvas` dependency. WASM
works cross-platform so we can include it in the main `package.json`.
This commit is contained in:
silverwind
2025-09-05 23:06:02 +02:00
committed by GitHub
parent 09d1f359d5
commit 89b4be057b
11 changed files with 24 additions and 1281 deletions

1
.gitignore vendored
View File

@@ -74,7 +74,6 @@ cpu.out
/tests/*.ini /tests/*.ini
/tests/**/*.git/**/*.sample /tests/**/*.git/**/*.sample
/node_modules /node_modules
/tools/node_modules
/.venv /.venv
/yarn.lock /yarn.lock
/yarn-error.log /yarn-error.log

View File

@@ -230,7 +230,7 @@ node-check:
.PHONY: clean-all .PHONY: clean-all
clean-all: clean ## delete backend, frontend and integration files clean-all: clean ## delete backend, frontend and integration files
rm -rf $(WEBPACK_DEST_ENTRIES) node_modules tools/node_modules rm -rf $(WEBPACK_DEST_ENTRIES) node_modules
.PHONY: clean .PHONY: clean
clean: ## delete backend and integration files clean: ## delete backend and integration files
@@ -847,10 +847,6 @@ node_modules: pnpm-lock.yaml
pnpm install --frozen-lockfile pnpm install --frozen-lockfile
@touch node_modules @touch node_modules
tools/node_modules: tools/package.json
cd tools && pnpm install
@touch tools/node_modules
.venv: uv.lock .venv: uv.lock
uv sync uv sync
@touch .venv @touch .venv
@@ -925,7 +921,7 @@ generate-gitignore: ## update gitignore files
$(GO) run build/generate-gitignores.go $(GO) run build/generate-gitignores.go
.PHONY: generate-images .PHONY: generate-images
generate-images: | node_modules tools/node_modules ## generate images (requires cairo development packages) generate-images: | node_modules ## generate images
cd tools && node generate-images.js $(TAGS) cd tools && node generate-images.js $(TAGS)
.PHONY: generate-manpage .PHONY: generate-manpage

View File

@@ -15,6 +15,7 @@
"@github/text-expander-element": "2.9.2", "@github/text-expander-element": "2.9.2",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
"@primer/octicons": "19.15.5", "@primer/octicons": "19.15.5",
"@resvg/resvg-wasm": "2.6.2",
"@silverwind/vue3-calendar-heatmap": "2.0.6", "@silverwind/vue3-calendar-heatmap": "2.0.6",
"@techknowlogick/license-checker-webpack-plugin": "0.3.0", "@techknowlogick/license-checker-webpack-plugin": "0.3.0",
"add-asset-webpack-plugin": "3.0.0", "add-asset-webpack-plugin": "3.0.0",

9
pnpm-lock.yaml generated
View File

@@ -35,6 +35,9 @@ importers:
'@primer/octicons': '@primer/octicons':
specifier: 19.15.5 specifier: 19.15.5
version: 19.15.5 version: 19.15.5
'@resvg/resvg-wasm':
specifier: 2.6.2
version: 2.6.2
'@silverwind/vue3-calendar-heatmap': '@silverwind/vue3-calendar-heatmap':
specifier: 2.0.6 specifier: 2.0.6
version: 2.0.6(tippy.js@6.3.7)(vue@3.5.18(typescript@5.8.3)) version: 2.0.6(tippy.js@6.3.7)(vue@3.5.18(typescript@5.8.3))
@@ -797,6 +800,10 @@ packages:
'@primer/octicons@19.15.5': '@primer/octicons@19.15.5':
resolution: {integrity: sha512-FCXPTlXlHvAS3rRBd1C/xVBYSYzPPwS8tNcUxnvUYK6L4/d+zUy2KExLtzW+L9xKo2z8J9uY+c1VCsNRf+b4MQ==} resolution: {integrity: sha512-FCXPTlXlHvAS3rRBd1C/xVBYSYzPPwS8tNcUxnvUYK6L4/d+zUy2KExLtzW+L9xKo2z8J9uY+c1VCsNRf+b4MQ==}
'@resvg/resvg-wasm@2.6.2':
resolution: {integrity: sha512-FqALmHI8D4o6lk/LRWDnhw95z5eO+eAa6ORjVg09YRR7BkcM6oPHU9uyC0gtQG5vpFLvgpeU4+zEAz2H8APHNw==}
engines: {node: '>= 10'}
'@rolldown/pluginutils@1.0.0-beta.29': '@rolldown/pluginutils@1.0.0-beta.29':
resolution: {integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==} resolution: {integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==}
@@ -5481,6 +5488,8 @@ snapshots:
dependencies: dependencies:
object-assign: 4.1.1 object-assign: 4.1.1
'@resvg/resvg-wasm@2.6.2': {}
'@rolldown/pluginutils@1.0.0-beta.29': {} '@rolldown/pluginutils@1.0.0-beta.29': {}
'@rollup/plugin-commonjs@22.0.2(rollup@2.79.2)': '@rollup/plugin-commonjs@22.0.2(rollup@2.79.2)':

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env node #!/usr/bin/env node
import {loadSVGFromString, Canvas, Rect, util} from 'fabric/node'; // eslint-disable-line import-x/no-unresolved import {initWasm, Resvg} from '@resvg/resvg-wasm';
import {optimize} from 'svgo'; import {optimize} from 'svgo';
import {readFile, writeFile} from 'node:fs/promises'; import {readFile, writeFile} from 'node:fs/promises';
import {argv, exit} from 'node:process'; import {argv, exit} from 'node:process';
@@ -27,37 +27,23 @@ async function generate(svg, path, {size, bg}) {
return; return;
} }
const {objects, options} = await loadSVGFromString(svg); const resvgJS = new Resvg(svg, {
const canvas = new Canvas(); fitTo: {
canvas.setDimensions({width: size, height: size}); mode: 'width',
const ctx = canvas.getContext('2d'); value: size,
ctx.scale(options.width ? (size / options.width) : 1, options.height ? (size / options.height) : 1); },
...(bg && {background: 'white'}),
if (bg) { });
canvas.add(new Rect({ const renderedImage = resvgJS.render();
left: 0, const pngBytes = renderedImage.asPng();
top: 0, await writeFile(outputFile, Buffer.from(pngBytes));
height: size * (1 / (size / options.height)),
width: size * (1 / (size / options.width)),
fill: 'white',
}));
}
canvas.add(util.groupSVGElements(objects, options));
canvas.renderAll();
let png = Buffer.from([]);
for await (const chunk of canvas.createPNGStream()) {
png = Buffer.concat([png, chunk]);
}
await writeFile(outputFile, png);
} }
async function main() { async function main() {
const gitea = argv.slice(2).includes('gitea'); const gitea = argv.slice(2).includes('gitea');
const logoSvg = await readFile(new URL('../assets/logo.svg', import.meta.url), 'utf8'); const logoSvg = await readFile(new URL('../assets/logo.svg', import.meta.url), 'utf8');
const faviconSvg = await readFile(new URL('../assets/favicon.svg', import.meta.url), 'utf8'); const faviconSvg = await readFile(new URL('../assets/favicon.svg', import.meta.url), 'utf8');
await initWasm(await readFile(new URL(import.meta.resolve('@resvg/resvg-wasm/index_bg.wasm'))));
await Promise.all([ await Promise.all([
generate(logoSvg, '../public/assets/img/logo.svg', {size: 32}), generate(logoSvg, '../public/assets/img/logo.svg', {size: 32}),

View File

@@ -1,21 +0,0 @@
{
"name": "gitea-tools",
"version": "1.0.0",
"description": "Build tools for Gitea",
"type": "module",
"private": true,
"dependencies": {
"fabric": "^6.7.1",
"svgo": "^4.0.0",
"fast-glob": "^3.3.3"
},
"optionalDependencies": {
"canvas": "^3.2.0"
},
"pnpm": {
"onlyBuiltDependencies": ["canvas"],
"overrides": {
"canvas": "3.2.0"
}
}
}

1227
tools/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff