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`.
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -74,7 +74,6 @@ cpu.out
 | 
			
		||||
/tests/*.ini
 | 
			
		||||
/tests/**/*.git/**/*.sample
 | 
			
		||||
/node_modules
 | 
			
		||||
/tools/node_modules
 | 
			
		||||
/.venv
 | 
			
		||||
/yarn.lock
 | 
			
		||||
/yarn-error.log
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						@@ -230,7 +230,7 @@ node-check:
 | 
			
		||||
 | 
			
		||||
.PHONY: clean-all
 | 
			
		||||
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
 | 
			
		||||
clean: ## delete backend and integration files
 | 
			
		||||
@@ -847,10 +847,6 @@ node_modules: pnpm-lock.yaml
 | 
			
		||||
	pnpm install --frozen-lockfile
 | 
			
		||||
	@touch node_modules
 | 
			
		||||
 | 
			
		||||
tools/node_modules: tools/package.json
 | 
			
		||||
	cd tools && pnpm install
 | 
			
		||||
	@touch tools/node_modules
 | 
			
		||||
 | 
			
		||||
.venv: uv.lock
 | 
			
		||||
	uv sync
 | 
			
		||||
	@touch .venv
 | 
			
		||||
@@ -925,7 +921,7 @@ generate-gitignore: ## update gitignore files
 | 
			
		||||
	$(GO) run build/generate-gitignores.go
 | 
			
		||||
 | 
			
		||||
.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)
 | 
			
		||||
 | 
			
		||||
.PHONY: generate-manpage
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@
 | 
			
		||||
    "@github/text-expander-element": "2.9.2",
 | 
			
		||||
    "@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
 | 
			
		||||
    "@primer/octicons": "19.15.5",
 | 
			
		||||
    "@resvg/resvg-wasm": "2.6.2",
 | 
			
		||||
    "@silverwind/vue3-calendar-heatmap": "2.0.6",
 | 
			
		||||
    "@techknowlogick/license-checker-webpack-plugin": "0.3.0",
 | 
			
		||||
    "add-asset-webpack-plugin": "3.0.0",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						@@ -35,6 +35,9 @@ importers:
 | 
			
		||||
      '@primer/octicons':
 | 
			
		||||
        specifier: 19.15.5
 | 
			
		||||
        version: 19.15.5
 | 
			
		||||
      '@resvg/resvg-wasm':
 | 
			
		||||
        specifier: 2.6.2
 | 
			
		||||
        version: 2.6.2
 | 
			
		||||
      '@silverwind/vue3-calendar-heatmap':
 | 
			
		||||
        specifier: 2.0.6
 | 
			
		||||
        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':
 | 
			
		||||
    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':
 | 
			
		||||
    resolution: {integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==}
 | 
			
		||||
 | 
			
		||||
@@ -5481,6 +5488,8 @@ snapshots:
 | 
			
		||||
    dependencies:
 | 
			
		||||
      object-assign: 4.1.1
 | 
			
		||||
 | 
			
		||||
  '@resvg/resvg-wasm@2.6.2': {}
 | 
			
		||||
 | 
			
		||||
  '@rolldown/pluginutils@1.0.0-beta.29': {}
 | 
			
		||||
 | 
			
		||||
  '@rollup/plugin-commonjs@22.0.2(rollup@2.79.2)':
 | 
			
		||||
 
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 3.8 KiB  | 
| 
		 Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 4.7 KiB  | 
| 
		 Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 4.2 KiB  | 
| 
		 Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 13 KiB  | 
@@ -1,5 +1,5 @@
 | 
			
		||||
#!/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 {readFile, writeFile} from 'node:fs/promises';
 | 
			
		||||
import {argv, exit} from 'node:process';
 | 
			
		||||
@@ -27,37 +27,23 @@ async function generate(svg, path, {size, bg}) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const {objects, options} = await loadSVGFromString(svg);
 | 
			
		||||
  const canvas = new Canvas();
 | 
			
		||||
  canvas.setDimensions({width: size, height: size});
 | 
			
		||||
  const ctx = canvas.getContext('2d');
 | 
			
		||||
  ctx.scale(options.width ? (size / options.width) : 1, options.height ? (size / options.height) : 1);
 | 
			
		||||
 | 
			
		||||
  if (bg) {
 | 
			
		||||
    canvas.add(new Rect({
 | 
			
		||||
      left: 0,
 | 
			
		||||
      top: 0,
 | 
			
		||||
      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);
 | 
			
		||||
  const resvgJS = new Resvg(svg, {
 | 
			
		||||
    fitTo: {
 | 
			
		||||
      mode: 'width',
 | 
			
		||||
      value: size,
 | 
			
		||||
    },
 | 
			
		||||
    ...(bg && {background: 'white'}),
 | 
			
		||||
  });
 | 
			
		||||
  const renderedImage = resvgJS.render();
 | 
			
		||||
  const pngBytes = renderedImage.asPng();
 | 
			
		||||
  await writeFile(outputFile, Buffer.from(pngBytes));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function main() {
 | 
			
		||||
  const gitea = argv.slice(2).includes('gitea');
 | 
			
		||||
  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');
 | 
			
		||||
  await initWasm(await readFile(new URL(import.meta.resolve('@resvg/resvg-wasm/index_bg.wasm'))));
 | 
			
		||||
 | 
			
		||||
  await Promise.all([
 | 
			
		||||
    generate(logoSvg, '../public/assets/img/logo.svg', {size: 32}),
 | 
			
		||||
 
 | 
			
		||||
@@ -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"
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||