chore: fix dependabot alerts and add dependency automation

This commit is contained in:
xixu-me committed 2026-03-17 15:23:12 +08:00
1 parent c1079cee40
commit d900d691f3
8 files changed
+691 -782

No files matched your search

+53
View File
@@ -0,0 +1,53 @@
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
target-branch: "main"
schedule:
interval: "weekly"
day: "monday"
time: "09:00"
timezone: "Asia/Shanghai"
open-pull-requests-limit: 5
labels:
- "dependencies"
- "npm"
commit-message:
prefix: "chore"
include: "scope"
groups:
production-dependencies:
dependency-type: "production"
update-types:
- "minor"
- "patch"
development-tooling:
dependency-type: "development"
update-types:
- "minor"
- "patch"
ignore:
- dependency-name: "react"
update-types:
- "version-update:semver-major"
- dependency-name: "react-dom"
update-types:
- "version-update:semver-major"
- dependency-name: "vite"
update-types:
- "version-update:semver-major"
- package-ecosystem: "github-actions"
directory: "/"
target-branch: "main"
schedule:
interval: "weekly"
day: "monday"
time: "09:30"
timezone: "Asia/Shanghai"
open-pull-requests-limit: 3
labels:
- "dependencies"
- "github-actions"
commit-message:
prefix: "chore"
include: "scope"
+36
View File
@@ -0,0 +1,36 @@
name: CI
on:
push:
branches:
- main
pull_request:
permissions:
contents: read
jobs:
verify:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Build
run: npm run build
- name: Audit dependencies
run: npm run audit
+39
View File
@@ -0,0 +1,39 @@
import js from '@eslint/js';
import tsParser from '@typescript-eslint/parser';
import tseslint from '@typescript-eslint/eslint-plugin';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';
import globals from 'globals';
export default [
{
ignores: ['dist/**'],
},
js.configs.recommended,
{
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
parser: tsParser,
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
globals: {
...globals.browser,
},
},
plugins: {
'@typescript-eslint': tseslint,
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...tseslint.configs.recommended.rules,
...reactHooks.configs.flat.recommended.rules,
...reactRefresh.configs.vite.rules,
},
},
];
+513 -749
View File
File diff suppressed because it is too large. Load diff
+9 -6
View File
@@ -5,14 +5,16 @@
"react-dom": "^18.2.0"
},
"devDependencies": {
"@eslint/js": "^9.39.4",
"@types/react": "^18.2.43",
"@types/react-dom": "^18.2.17",
"@typescript-eslint/eslint-plugin": "^6.14.0",
"@typescript-eslint/parser": "^6.14.0",
"@typescript-eslint/eslint-plugin": "^8.57.1",
"@typescript-eslint/parser": "^8.57.1",
"@vitejs/plugin-react": "^4.2.1",
"eslint": "^8.55.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5",
"eslint": "^9.39.4",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.26",
"globals": "^16.5.0",
"typescript": "^5.2.2",
"vite": "^7.2.2"
},
@@ -21,7 +23,8 @@
"scripts": {
"build": "tsc && vite build",
"dev": "vite",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"lint": "eslint . --report-unused-disable-directives --max-warnings 0",
"audit": "npm audit --audit-level=moderate",
"preview": "vite preview"
},
"type": "module",
+15 -8
View File
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from 'react';
import { useEffect, useMemo, useRef } from 'react';
import { renderQRToCanvas } from '../lib/qrGenerator';
import { QRCodeInstance, QRConfig, QRInfo } from '../types/qr';
import './PreviewPanel.css';
@@ -14,14 +14,23 @@ interface PreviewPanelProps {
export function PreviewPanel({ qr, config, info, onExportPNG, onExportSVG, onExportConfig }: PreviewPanelProps) {
const canvasRef = useRef<HTMLCanvasElement>(null);
const [isAnimating, setIsAnimating] = useState(false);
const previewKey = useMemo(
() => JSON.stringify({
content: config.content,
version: info?.version ?? config.version,
errorCorrectionLevel: config.errorCorrectionLevel,
maskPattern: config.maskPattern,
quietZone: config.quietZone,
moduleStyle: config.moduleStyle,
foregroundColor: config.foregroundColor,
backgroundColor: config.backgroundColor,
}),
[config, info?.version],
);
useEffect(() => {
if (!qr || !canvasRef.current) return;
setIsAnimating(true);
const timer = setTimeout(() => setIsAnimating(false), 500);
try {
const canvas = renderQRToCanvas(qr, config, 512);
const ctx = canvasRef.current.getContext('2d');
@@ -33,8 +42,6 @@ export function PreviewPanel({ qr, config, info, onExportPNG, onExportSVG, onExp
} catch (error) {
console.error('Error rendering QR code:', error);
}
return () => clearTimeout(timer);
}, [qr, config]);
if (!qr || !info) {
@@ -53,7 +60,7 @@ export function PreviewPanel({ qr, config, info, onExportPNG, onExportSVG, onExp
<div className="preview-panel glass-card fade-in">
<h2 className="panel-title">Preview</h2>
<div className={`preview-container ${isAnimating ? 'animating' : ''}`}>
<div key={previewKey} className="preview-container animating">
<canvas ref={canvasRef} className="preview-canvas" />
</div>
+21 -17
View File
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useState } from "react";
import { useCallback, useMemo, useState } from "react";
import { generateQRCode } from "../lib/qrGenerator";
import { QRCodeInstance, QRConfig, QRInfo } from "../types/qr";
@@ -16,9 +16,6 @@ const DEFAULT_CONFIG: QRConfig = {
export function useQRCode() {
const [config, setConfig] = useState<QRConfig>(DEFAULT_CONFIG);
const [qr, setQr] = useState<QRCodeInstance | null>(null);
const [info, setInfo] = useState<QRInfo | null>(null);
const [error, setError] = useState<string | null>(null);
const updateConfig = useCallback((updates: Partial<QRConfig>) => {
setConfig((prev) => ({ ...prev, ...updates }));
@@ -28,25 +25,32 @@ export function useQRCode() {
setConfig(DEFAULT_CONFIG);
}, []);
useEffect(() => {
const { qr, info, error } = useMemo<{
qr: QRCodeInstance | null;
info: QRInfo | null;
error: string | null;
}>(() => {
if (!config.content) {
setQr(null);
setInfo(null);
setError(null);
return;
return {
qr: null,
info: null,
error: null,
};
}
try {
const result = generateQRCode(config);
setQr(result.qr);
setInfo(result.info);
setError(null);
return {
qr: result.qr,
info: result.info,
error: null,
};
} catch (err) {
setError(
err instanceof Error ? err.message : "Failed to generate QR code"
);
setQr(null);
setInfo(null);
return {
qr: null,
info: null,
error: err instanceof Error ? err.message : "Failed to generate QR code",
};
}
}, [config]);
+5 -2
View File
@@ -6,6 +6,8 @@ import {
QRInfo,
} from "../types/qr";
type QRCodeVersion = Parameters<typeof qrcode>[0];
// Map error correction levels to library constants
const EC_LEVEL_MAP: Record<ErrorCorrectionLevel, ErrorCorrectionLevel> = {
L: "L",
@@ -79,7 +81,7 @@ export function generateQRCode(config: QRConfig): {
}
// Create QR code
const qr = qrcode(version as any, ecLevel);
const qr = qrcode(version as QRCodeVersion, ecLevel);
qr.addData(config.content);
qr.make();
@@ -185,7 +187,7 @@ export function renderQRToCanvas(
case "square":
ctx.fillRect(x, y, moduleSize, moduleSize);
break;
case "rounded":
case "rounded": {
ctx.beginPath();
const radius = moduleSize * 0.3;
ctx.moveTo(x + radius, y);
@@ -205,6 +207,7 @@ export function renderQRToCanvas(
ctx.closePath();
ctx.fill();
break;
}
case "dots":
ctx.beginPath();
ctx.arc(