Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .github/workflows/_ci-node.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
# This workflow is centrally managed in https://github.com/LizardByte/.github/
# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in
# the above-mentioned repo.

# To use, add the `npm-pkg` repository label to identify repositories that should trigger this workflow.

# This will run standard CI for Node.js/npm/TypeScript projects.

name: CI-Node
permissions:
contents: write # required for release_setup action

on:
push:
branches:
- master
pull_request:
branches:
- master

concurrency:
group: "${{ github.workflow }}-${{ github.ref }}"
cancel-in-progress: true

jobs:
call-ci-node:
name: CI-Node
uses: LizardByte/.github/.github/workflows/__call-ci-node.yml@master
if: ${{ github.repository != 'LizardByte/.github' }}
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
GH_TOKEN: ${{ secrets.GH_BOT_TOKEN }}
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ node_modules/
package-lock.json

dist/

# coverage
coverage/
junit.xml
11 changes: 11 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@ github_accounts=(

output_dir="$(pwd)/dist"

# Debug output for CI environment
echo "GITHUB_JOB: ${GITHUB_JOB:-not set}"

# Check if we're running in a GitHub Actions job named "build"
# If so, skip all token-dependent sections since secrets won't be available
if [[ "${GITHUB_JOB}" = "build" ]]; then
echo "Running in GitHub Actions 'build' job - skipping all token-dependent sections"
echo "Done!"
exit 0
fi

echo "Building sponsors..."
pushd configs/sponsors || exit 1
npx contribkit --outputDir="${output_dir}" -w=800 --name=sponsors --force
Expand Down
44 changes: 1 addition & 43 deletions configs/sponsors/contribkit.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
defineConfig,
tierPresets,
} from '@lizardbyte/contribkit'
import { extractSvgDimensions, createWrappedSponsorSvg } from './svg-utils.js'

const createThemeAwareSvgStyle = (pathClass: string, lightColor: string = '#000000', darkColor: string = '#ffffff') => `
<style>
Expand Down Expand Up @@ -127,49 +128,6 @@ const specialSupporters = [
},
];

// Function to create wrapped SVG for a special sponsor
function createWrappedSponsorSvg(
sponsor: { name: string, url: string },
svgContent: string,
svgWidth: number,
svgHeight: number,
height: number,
x: number,
y: number
): string {
const scale = height ? height / svgHeight : 1;
const scaledWidth = svgWidth * scale;
const scaledHeight = svgHeight * scale;

return `
<a xlink:href="${sponsor.url}" class="contribkit-link" target="_blank" id="${sponsor.name.replace(/\s+/g, '')}">
<svg x="${x}" y="${y}" width="${scaledWidth}" height="${scaledHeight}" viewBox="0 0 ${svgWidth} ${svgHeight}">
<rect width="${svgWidth}" height="${svgHeight}" fill="transparent" />
${svgContent}
</svg>
</a>`;
}

// Function to extract dimensions from SVG content
function extractSvgDimensions(svgContent: string): { width: number, height: number } {
// Try to get dimensions from viewBox first
const viewBoxMatch = svgContent.match(/viewBox=['"]([^'"]*)['"]/);
if (viewBoxMatch) {
const [, minX, minY, width, height] = viewBoxMatch[1].split(/\s+/).map(Number);
if (!isNaN(width) && !isNaN(height)) {
return { width, height };
}
}

// Try to get from width/height attributes
const widthMatch = svgContent.match(/width=['"]([^'"]*)['"]/);
const heightMatch = svgContent.match(/height=['"]([^'"]*)['"]/);

const width = widthMatch ? parseInt(widthMatch[1]) : 200;
const height = heightMatch ? parseInt(heightMatch[1]) : 100;

return { width, height };
}

export default defineConfig({
tiers: [
Expand Down
62 changes: 62 additions & 0 deletions configs/sponsors/svg-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* Utility functions for SVG dimension extraction
*/

/**
* Extracts dimensions from SVG content by parsing viewBox or width/height attributes
* @param svgContent - The SVG content as a string
* @returns Object containing width and height
*/
export function extractSvgDimensions(svgContent: string): { width: number; height: number } {
// Try to get dimensions from viewBox first
const viewBoxMatch = svgContent.match(/viewBox=['"]([^'"]*)['"]/);

Check warning on line 12 in configs/sponsors/svg-utils.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use the "RegExp.exec()" method instead.

See more on https://sonarcloud.io/project/issues?id=LizardByte_contributors&issues=AZrVIwd0dt3rxBuW7xso&open=AZrVIwd0dt3rxBuW7xso&pullRequest=49
if (viewBoxMatch) {
const [minX, minY, width, height] = viewBoxMatch[1].split(/\s+/).map(Number);

Check warning on line 14 in configs/sponsors/svg-utils.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this useless assignment to variable "minX".

See more on https://sonarcloud.io/project/issues?id=LizardByte_contributors&issues=AZrVIwd0dt3rxBuW7xsp&open=AZrVIwd0dt3rxBuW7xsp&pullRequest=49

Check warning on line 14 in configs/sponsors/svg-utils.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this useless assignment to variable "minY".

See more on https://sonarcloud.io/project/issues?id=LizardByte_contributors&issues=AZrVIwd0dt3rxBuW7xsq&open=AZrVIwd0dt3rxBuW7xsq&pullRequest=49
// Check that both width and height are valid numbers (not NaN and not undefined)
if (!Number.isNaN(width) && !Number.isNaN(height) && width !== undefined && height !== undefined) {
return { width, height };
}
}

// Try to get from width/height attributes
const widthMatch = svgContent.match(/width=['"]([^'"]*)['"]/);

Check warning on line 22 in configs/sponsors/svg-utils.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use the "RegExp.exec()" method instead.

See more on https://sonarcloud.io/project/issues?id=LizardByte_contributors&issues=AZrVIwd0dt3rxBuW7xsr&open=AZrVIwd0dt3rxBuW7xsr&pullRequest=49
const heightMatch = svgContent.match(/height=['"]([^'"]*)['"]/);

Check warning on line 23 in configs/sponsors/svg-utils.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use the "RegExp.exec()" method instead.

See more on https://sonarcloud.io/project/issues?id=LizardByte_contributors&issues=AZrVIwd0dt3rxBuW7xss&open=AZrVIwd0dt3rxBuW7xss&pullRequest=49

const width = widthMatch ? Number.parseInt(widthMatch[1], 10) : 200;
const height = heightMatch ? Number.parseInt(heightMatch[1], 10) : 100;

return { width, height };
}

/**
* Creates a wrapped SVG element for a sponsor with positioning and scaling
* @param sponsor - Sponsor object with name and url
* @param svgContent - The SVG content to wrap
* @param svgWidth - Original width of the SVG
* @param svgHeight - Original height of the SVG
* @param height - Target height for the scaled SVG
* @param x - X position for the SVG
* @param y - Y position for the SVG
* @returns Wrapped SVG string
*/
export function createWrappedSponsorSvg(
sponsor: { name: string; url: string },
svgContent: string,
svgWidth: number,
svgHeight: number,
height: number,
x: number,
y: number
): string {
const scale = height ? height / svgHeight : 1;
const scaledWidth = svgWidth * scale;
const scaledHeight = svgHeight * scale;

return `
<a xlink:href="${sponsor.url}" class="contribkit-link" target="_blank" id="${sponsor.name.replaceAll(/\s+/g, '')}">
<svg x="${x}" y="${y}" width="${scaledWidth}" height="${scaledHeight}" viewBox="0 0 ${svgWidth} ${svgHeight}">
<rect width="${svgWidth}" height="${svgHeight}" fill="transparent" />
${svgContent}
</svg>
</a>`;
}
33 changes: 33 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import globals from "globals";
import pluginJs from "@eslint/js";

export default [
pluginJs.configs.recommended,
{
ignores: [
"coverage/**",
"dist/**",
],
},
{
languageOptions: {
globals: {
...globals.browser,
...globals.node,
"require": "readonly",
},
},
plugins: {
jest: {
extends: ["eslint:recommended"],
rules: {
"jest/no-disabled-tests": "error",
"jest/no-focused-tests": "error",
"jest/no-identical-title": "error",
"jest/prefer-to-have-length": "error",
"jest/valid-expect": "error",
},
},
},
},
];
61 changes: 59 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,69 @@
{
"name": "@lizardbyte/contributors",
"type": "module",
"repository": {
"type": "git",
"url": "git+https://github.com/LizardByte/contributors.git"
},
"version": "0.0.0",
"description": "contribkit for LizardByte",
"license": "MIT",
"funding": "https://app.lizardbyte.dev",
"homepage": "https://github.com/LizardByte/contributors#readme",
"bugs": {
"url": "https://github.com/LizardByte/contributors/issues"
},
"keywords": [
"contributors",
"sponsors",
"github-sponsors"
],
"scripts": {
"build": "bash build.sh"
"build": "bash build.sh",
"test": "npm-run-all test:unit test:report test:lint test:typecheck",
"test:unit": "jest --coverage",
"test:report": "jest --reporters=jest-junit",
"test:lint": "eslint .",
"test:typecheck": "tsc --noEmit"
},
"dependencies": {
"@lizardbyte/contribkit": "2025.1130.1103"
},
"devDependencies": {
"@types/node": "^24.0.0"
"@types/jest": "30.0.0",
"@types/node": "24.10.1",
"eslint": "9.39.1",
"eslint-plugin-jest": "29.2.1",
"jest": "30.2.0",
"jest-junit": "16.0.0",
"npm-run-all": "4.1.5",
"ts-jest": "29.4.5",
"typescript": "5.9.3"
},
"jest": {
"preset": "ts-jest/presets/default-esm",
"testEnvironment": "node",
"extensionsToTreatAsEsm": [
".ts"
],
"moduleNameMapper": {
"^(\\.{1,2}/.*)\\.js$": "$1"
},
"transform": {
"^.+\\.tsx?$": [
"ts-jest",
{
"useESM": true
}
]
},
"testMatch": [
"**/tests/**/*.test.ts",
"**/tests/**/*.spec.ts"
],
"collectCoverageFrom": [
"configs/**/*.ts",
"!configs/**/*.d.ts"
]
}
}
Loading