diff options
Diffstat (limited to '')
-rwxr-xr-x | tools/generate-images.js | 79 | ||||
-rwxr-xr-x | tools/generate-svg.js | 70 | ||||
-rwxr-xr-x | tools/lint-go-gopls.sh | 23 | ||||
-rwxr-xr-x | tools/lint-templates-svg.js | 26 | ||||
-rw-r--r-- | tools/watch.sh | 8 |
5 files changed, 206 insertions, 0 deletions
diff --git a/tools/generate-images.js b/tools/generate-images.js new file mode 100755 index 00000000..0bd3af29 --- /dev/null +++ b/tools/generate-images.js @@ -0,0 +1,79 @@ +#!/usr/bin/env node +import imageminZopfli from 'imagemin-zopfli'; // eslint-disable-line i/no-unresolved +import {loadSVGFromString, Canvas, Rect, util} from 'fabric/node'; // eslint-disable-line i/no-unresolved +import {optimize} from 'svgo'; +import {readFile, writeFile} from 'node:fs/promises'; +import {argv, exit} from 'node:process'; + +function doExit(err) { + if (err) console.error(err); + exit(err ? 1 : 0); +} + +async function generate(svg, path, {size, bg}) { + const outputFile = new URL(path, import.meta.url); + + if (String(outputFile).endsWith('.svg')) { + const {data} = optimize(svg, { + plugins: [ + 'preset-default', + 'removeDimensions', + { + name: 'addAttributesToSVGElement', + params: {attributes: [{width: size}, {height: size}]}, + }, + ], + }); + await writeFile(outputFile, data); + 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]); + } + + png = await imageminZopfli({more: true})(png); + await writeFile(outputFile, png); +} + +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 Promise.all([ + generate(logoSvg, '../public/assets/img/logo.svg', {size: 32}), + generate(logoSvg, '../public/assets/img/logo.png', {size: 512}), + generate(faviconSvg, '../public/assets/img/favicon.svg', {size: 32}), + generate(faviconSvg, '../public/assets/img/favicon.png', {size: 180}), + generate(logoSvg, '../public/assets/img/avatar_default.png', {size: 200}), + generate(logoSvg, '../public/assets/img/apple-touch-icon.png', {size: 180, bg: true}), + gitea && generate(logoSvg, '../public/assets/img/gitea.svg', {size: 32}), + ]); +} + +try { + doExit(await main()); +} catch (err) { + doExit(err); +} diff --git a/tools/generate-svg.js b/tools/generate-svg.js new file mode 100755 index 00000000..1c5851e7 --- /dev/null +++ b/tools/generate-svg.js @@ -0,0 +1,70 @@ +#!/usr/bin/env node +import fastGlob from 'fast-glob'; +import {optimize} from 'svgo'; +import {parse} from 'node:path'; +import {readFile, writeFile, mkdir} from 'node:fs/promises'; +import {fileURLToPath} from 'node:url'; +import {exit} from 'node:process'; + +const glob = (pattern) => fastGlob.sync(pattern, { + cwd: fileURLToPath(new URL('..', import.meta.url)), + absolute: true, +}); + +function doExit(err) { + if (err) console.error(err); + exit(err ? 1 : 0); +} + +async function processFile(file, {prefix, fullName} = {}) { + let name; + if (fullName) { + name = fullName; + } else { + name = parse(file).name; + if (prefix) name = `${prefix}-${name}`; + if (prefix === 'octicon') name = name.replace(/-[0-9]+$/, ''); // chop of '-16' on octicons + } + + // Set the `xmlns` attribute so that the files are displayable in standalone documents + // The svg backend module will strip the attribute during startup for inline display + const {data} = optimize(await readFile(file, 'utf8'), { + plugins: [ + {name: 'preset-default'}, + {name: 'removeDimensions'}, + {name: 'prefixIds', params: {prefix: () => name}}, + {name: 'addClassesToSVGElement', params: {classNames: ['svg', name]}}, + { + name: 'addAttributesToSVGElement', params: { + attributes: [ + {'xmlns': 'http://www.w3.org/2000/svg'}, + {'width': '16'}, {'height': '16'}, {'aria-hidden': 'true'}, + ], + }, + }, + ], + }); + + await writeFile(fileURLToPath(new URL(`../public/assets/img/svg/${name}.svg`, import.meta.url)), data); +} + +function processFiles(pattern, opts) { + return glob(pattern).map((file) => processFile(file, opts)); +} + +async function main() { + try { + await mkdir(fileURLToPath(new URL('../public/assets/img/svg', import.meta.url)), {recursive: true}); + } catch {} + + await Promise.all([ + ...processFiles('node_modules/@primer/octicons/build/svg/*-16.svg', {prefix: 'octicon'}), + ...processFiles('web_src/svg/*.svg'), + ]); +} + +try { + doExit(await main()); +} catch (err) { + doExit(err); +} diff --git a/tools/lint-go-gopls.sh b/tools/lint-go-gopls.sh new file mode 100755 index 00000000..4bb69f4c --- /dev/null +++ b/tools/lint-go-gopls.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -uo pipefail + +cd "$(dirname -- "${BASH_SOURCE[0]}")" && cd .. + +IGNORE_PATTERNS=( + "is deprecated" # TODO: fix these +) + +# lint all go files with 'gopls check' and look for lines starting with the +# current absolute path, indicating a error was found. This is neccessary +# because the tool does not set non-zero exit code when errors are found. +# ref: https://github.com/golang/go/issues/67078 +ERROR_LINES=$("$GO" run "$GOPLS_PACKAGE" check "$@" 2>/dev/null | grep -E "^$PWD" | grep -vFf <(printf '%s\n' "${IGNORE_PATTERNS[@]}")); +NUM_ERRORS=$(echo -n "$ERROR_LINES" | wc -l) + +if [ "$NUM_ERRORS" -eq "0" ]; then + exit 0; +else + echo "$ERROR_LINES" + echo "Found $NUM_ERRORS 'gopls check' errors" + exit 1; +fi diff --git a/tools/lint-templates-svg.js b/tools/lint-templates-svg.js new file mode 100755 index 00000000..72f75640 --- /dev/null +++ b/tools/lint-templates-svg.js @@ -0,0 +1,26 @@ +#!/usr/bin/env node +import {readdirSync, readFileSync} from 'node:fs'; +import {parse, relative} from 'node:path'; +import {fileURLToPath} from 'node:url'; +import {exit} from 'node:process'; +import fastGlob from 'fast-glob'; + +const knownSvgs = new Set(); +for (const file of readdirSync(new URL('../public/assets/img/svg', import.meta.url))) { + knownSvgs.add(parse(file).name); +} + +const rootPath = fileURLToPath(new URL('..', import.meta.url)); +let hadErrors = false; + +for (const file of fastGlob.sync(fileURLToPath(new URL('../templates/**/*.tmpl', import.meta.url)))) { + const content = readFileSync(file, 'utf8'); + for (const [_, name] of content.matchAll(/svg ["'`]([^"'`]+)["'`]/g)) { + if (!knownSvgs.has(name)) { + console.info(`SVG "${name}" not found, used in ${relative(rootPath, file)}`); + hadErrors = true; + } + } +} + +exit(hadErrors ? 1 : 0); diff --git a/tools/watch.sh b/tools/watch.sh new file mode 100644 index 00000000..5e8defa4 --- /dev/null +++ b/tools/watch.sh @@ -0,0 +1,8 @@ +#!/bin/bash +set -euo pipefail + +make --no-print-directory watch-frontend & +make --no-print-directory watch-backend & + +trap 'kill $(jobs -p)' EXIT +wait |