chore: dep pkg upgrade

- Updated Node version in Dockerfile and .npmrc.
- Upgraded various dependencies in package.json, including react-dropzone, react-router, and eslint.
- Added pnpm-workspace.yaml for workspace management.
- Adjusted TypeScript configurations and test utilities for compatibility.
- Enhanced documentation for installation instructions and TypeScript toolchain.
This commit is contained in:
鲁树人
2026-10-21 10:51:06 +09:00
parent 4df197ac4d
commit b142c1ba34
18 changed files with 1637 additions and 1829 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
npm_config_registry: https://registry.npmmirror.com pnpm_config_registry: https://registry.npmmirror.com
steps: steps:
- name: Check out repository code - name: Check out repository code
uses: actions/checkout@v4 uses: actions/checkout@v4
-2
View File
@@ -1,3 +1 @@
use-node-version=24.7.0
engine-strict=true
@unlock-music:registry=https://git.um-react.app/api/packages/um/npm/ @unlock-music:registry=https://git.um-react.app/api/packages/um/npm/
+2 -2
View File
@@ -1,4 +1,4 @@
FROM node:22-slim AS build FROM node:24.22.0-slim AS build
ENV PNPM_HOME="/p" ENV PNPM_HOME="/p"
ENV PATH="$PNPM_HOME:$PATH" ENV PATH="$PNPM_HOME:$PATH"
WORKDIR /app WORKDIR /app
@@ -9,7 +9,7 @@ RUN corepack enable pnpm \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
COPY package.json pnpm-lock.yaml .npmrc ./ COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./
RUN pnpm exec true RUN pnpm exec true
COPY . . COPY . .
RUN pnpm install --frozen-lockfile RUN pnpm install --frozen-lockfile
+6 -2
View File
@@ -4,8 +4,8 @@
## 安装依赖 ## 安装依赖
- 安装 Node v16.17 或更高,推荐当前最新的 Node LTS 版本。 - 安装 Node 24.22.0;pnpm 也会根据 `package.json` 中的 `devEngines.runtime` 自动获取该版本。
- 安装/激活 `pnpm` [^1]:`corepack prepare pnpm@latest --activate` - 安装/激活项目指定的 `pnpm` [^1]:`corepack prepare pnpm@12.14.0 --activate`
- 安装软件依赖:`pnpm i --frozen-lockfile` - 安装软件依赖:`pnpm i --frozen-lockfile`
[^1]: 参考 pnpm 说明「[使用 Corepack 安装](https://pnpm.io/zh/installation#使用-corepack-安装)」。 [^1]: 参考 pnpm 说明「[使用 Corepack 安装](https://pnpm.io/zh/installation#使用-corepack-安装)」。
@@ -34,6 +34,10 @@ pnpm build
[vite-preview-url]: http://localhost:4173/ [vite-preview-url]: http://localhost:4173/
### TypeScript 工具链
编译和 ESLint 统一使用 TypeScript 6.x,暂不升级到 TypeScript 7。
## 打包 `.zip` ## 打包 `.zip`
建议在 Linux 环境下执行,可参考 `.drone.yml` CI 文件。 建议在 Linux 环境下执行,可参考 `.drone.yml` CI 文件。
+26 -34
View File
@@ -22,26 +22,26 @@
"classnames": "^2.5.1", "classnames": "^2.5.1",
"fflate": "^0.8.3", "fflate": "^0.8.3",
"idb-keyval": "^6.3.0", "idb-keyval": "^6.3.0",
"nanoid": "^5.1.16", "nanoid": "^6.0.2",
"radash": "^12.1.1", "radash": "^12.1.1",
"react": "^19.3.1", "react": "^19.3.1",
"react-dom": "^19.3.1", "react-dom": "^19.3.1",
"react-dropzone": "^14.4.1", "react-dropzone": "^20.1.4",
"react-icons": "^5.7.0", "react-icons": "^5.7.0",
"react-redux": "^9.3.0", "react-redux": "^9.3.0",
"react-router": "^7.18.4", "react-router": "^8.5.0",
"react-syntax-highlighter": "^15.6.6", "react-syntax-highlighter": "^16.1.2",
"react-toastify": "^11.1.0", "react-toastify": "^11.1.0",
"sql.js": "^1.15.1" "sql.js": "^1.15.1"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.39.5", "@eslint/js": "^10.0.1",
"@rollup/plugin-replace": "^6.0.3", "@rollup/plugin-replace": "^6.0.3",
"@tailwindcss/vite": "^4.3.3", "@tailwindcss/vite": "^4.3.3",
"@testing-library/jest-dom": "^6.9.1", "@testing-library/jest-dom": "^7.0.1",
"@testing-library/react": "^16.3.3", "@testing-library/react": "^16.3.3",
"@testing-library/user-event": "^14.6.8", "@testing-library/user-event": "^14.6.8",
"@types/node": "^24.19.2", "@types/node": "^26.6.5",
"@types/react": "^19.3.0", "@types/react": "^19.3.0",
"@types/react-dom": "^19.3.0", "@types/react-dom": "^19.3.0",
"@types/react-syntax-highlighter": "^15.5.13", "@types/react-syntax-highlighter": "^15.5.13",
@@ -50,18 +50,18 @@
"@types/wicg-file-system-access": "^2023.10.7", "@types/wicg-file-system-access": "^2023.10.7",
"@typescript-eslint/eslint-plugin": "^8.72.0", "@typescript-eslint/eslint-plugin": "^8.72.0",
"@typescript-eslint/parser": "^8.72.0", "@typescript-eslint/parser": "^8.72.0",
"@vitejs/plugin-react": "^5.2.0", "@vitejs/plugin-react": "^6.1.2",
"@vitest/coverage-v8": "^3.2.7", "@vitest/coverage-v8": "^5.0.5",
"@vitest/ui": "^3.2.7", "@vitest/ui": "^5.0.5",
"daisyui": "^5.7.50", "daisyui": "^5.7.50",
"eslint": "^9.39.5", "eslint": "^10.13.0",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-react-hooks": "^7.1.1", "eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-refresh": "^0.4.26", "eslint-plugin-react-refresh": "^0.5.7",
"globals": "^16.5.0", "globals": "^17.13.0",
"husky": "^9.1.7", "husky": "^9.1.7",
"jsdom": "^27.4.0", "jsdom": "^30.1.3",
"lint-staged": "^16.4.0", "lint-staged": "^17.6.0",
"prettier": "^3.9.10", "prettier": "^3.9.10",
"rollup": "^4.64.12", "rollup": "^4.64.12",
"sass": "^1.105.2", "sass": "^1.105.2",
@@ -69,13 +69,12 @@
"tailwindcss": "^4.3.3", "tailwindcss": "^4.3.3",
"tar-stream": "^3.2.2", "tar-stream": "^3.2.2",
"terser": "^5.51.2", "terser": "^5.51.2",
"typescript": "^5.9.3", "typescript": "^6.0.3",
"typescript-eslint": "^8.72.0", "typescript-eslint": "^8.72.0",
"vite": "^7.3.7", "vite": "^8.3.4",
"vite-plugin-pwa": "^1.3.0", "vite-plugin-pwa": "^2.0.0",
"vite-plugin-top-level-await": "^1.6.0",
"vite-plugin-wasm": "^3.6.0", "vite-plugin-wasm": "^3.6.0",
"vitest": "^3.2.7", "vitest": "^5.0.5",
"workbox-build": "^7.4.1", "workbox-build": "^7.4.1",
"workbox-window": "^7.4.1" "workbox-window": "^7.4.1"
}, },
@@ -92,19 +91,12 @@
"printWidth": 120, "printWidth": 120,
"tabWidth": 2 "tabWidth": 2
}, },
"pnpm": { "devEngines": {
"patchedDependencies": { "runtime": {
"sql.js": "patches/sql.js.patch" "name": "node",
}, "version": "24.22.0",
"overrides": { "onFail": "download"
"sourcemap-codec": "npm:@jridgewell/[email protected]" }
},
"onlyBuiltDependencies": [
"@swc/core",
"@tailwindcss/oxide",
"esbuild",
"simple-git-hooks"
]
}, },
"packageManager": "pnpm@10.15.1" "packageManager": "pnpm@12.14.0"
} }
+1528 -1762
View File
File diff suppressed because it is too large Load Diff
+16
View File
@@ -0,0 +1,16 @@
engineStrict: true
patchedDependencies:
sql.js: patches/sql.js.patch
overrides:
sourcemap-codec: npm:@jridgewell/[email protected]
allowBuilds:
'@parcel/watcher': false
'@swc/core': true
'@tailwindcss/oxide': true
esbuild: true
simple-git-hooks: true
minimumReleaseAge: 0
@@ -0,0 +1,33 @@
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { FileInput } from '../FileInput';
test('receives multiple files from the file picker', async () => {
const onReceiveFiles = vi.fn();
const files = [new File(['first'], 'first.qmc3'), new File(['second'], 'second.mflac')];
const { container } = render(<FileInput onReceiveFiles={onReceiveFiles}>Choose files</FileInput>);
const input = container.querySelector('input');
if (!input) throw new Error('File input is missing');
await userEvent.upload(input, files);
await waitFor(() => expect(onReceiveFiles).toHaveBeenCalledOnce());
expect(onReceiveFiles.mock.calls[0][0]).toEqual(files);
});
test('receives multiple dropped files', async () => {
const onReceiveFiles = vi.fn();
const files = [new File(['first'], 'first.kgm'), new File(['second'], 'second.ncm')];
render(<FileInput onReceiveFiles={onReceiveFiles}>Drop files</FileInput>);
fireEvent.drop(screen.getByText('Drop files'), {
dataTransfer: {
files,
items: files.map((file) => ({ kind: 'file', type: file.type, getAsFile: () => file })),
types: ['Files'],
},
});
await waitFor(() => expect(onReceiveFiles).toHaveBeenCalledOnce());
expect(onReceiveFiles.mock.calls[0][0]).toEqual(files);
});
+1 -1
View File
@@ -1,4 +1,4 @@
import '@testing-library/jest-dom'; import '@testing-library/jest-dom/vitest';
// FIXME: Use something like jsdom-worker? // FIXME: Use something like jsdom-worker?
// see: https://github.com/developit/jsdom-worker // see: https://github.com/developit/jsdom-worker
+1 -1
View File
@@ -17,7 +17,7 @@ export function renderWithProviders(
ui: React.ReactElement, ui: React.ReactElement,
{ preloadedState = {}, store = setupStore(preloadedState), ...renderOptions }: ExtendedRenderOptions = {}, { preloadedState = {}, store = setupStore(preloadedState), ...renderOptions }: ExtendedRenderOptions = {},
) { ) {
function Wrapper({ children }: PropsWithChildren<unknown>): JSX.Element { function Wrapper({ children }: PropsWithChildren<unknown>): React.JSX.Element {
return <Provider store={store}>{children}</Provider>; return <Provider store={store}>{children}</Provider>;
} }
+1 -1
View File
@@ -1,6 +1,6 @@
import { MMKVParser } from '../MMKVParser'; import { MMKVParser } from '../MMKVParser';
const makeViewFromBuffer = (buff: Buffer) => const makeViewFromBuffer = (buff: Buffer<ArrayBuffer>) =>
new DataView(buff.buffer.slice(buff.byteOffset, buff.byteOffset + buff.byteLength)); new DataView(buff.buffer.slice(buff.byteOffset, buff.byteOffset + buff.byteLength));
test('throw error on broken file', () => { test('throw error on broken file', () => {
+1 -1
View File
@@ -1,7 +1,7 @@
import { readFileSync } from 'node:fs'; import { readFileSync } from 'node:fs';
import { parseAndroidKuwoEKey, parseIosKuwoEKey } from '../kuwo'; import { parseAndroidKuwoEKey, parseIosKuwoEKey } from '../kuwo';
const makeViewFromBuffer = (buff: Buffer) => const makeViewFromBuffer = (buff: Buffer<ArrayBuffer>) =>
new DataView(buff.buffer.slice(buff.byteOffset, buff.byteOffset + buff.byteLength)); new DataView(buff.buffer.slice(buff.byteOffset, buff.byteOffset + buff.byteLength));
test('parse kuwo android ekey mmkv file "cn.kuwo.player.mmkv.defaultconfig"', () => { test('parse kuwo android ekey mmkv file "cn.kuwo.player.mmkv.defaultconfig"', () => {
+1 -1
View File
@@ -1,7 +1,7 @@
import { readFileSync } from 'node:fs'; import { readFileSync } from 'node:fs';
import { parseAndroidQmEKey } from '../qm'; import { parseAndroidQmEKey } from '../qm';
const makeViewFromBuffer = (buff: Buffer) => const makeViewFromBuffer = (buff: Buffer<ArrayBuffer>) =>
new DataView(buff.buffer.slice(buff.byteOffset, buff.byteOffset + buff.byteLength)); new DataView(buff.buffer.slice(buff.byteOffset, buff.byteOffset + buff.byteLength));
test('parse qm mmkv file', () => { test('parse qm mmkv file', () => {
+1 -1
View File
@@ -3,7 +3,7 @@ import { Plugin } from 'vite';
export const base64Loader: Plugin = { export const base64Loader: Plugin = {
name: 'base64-loader', name: 'base64-loader',
async transform(_: unknown, id: string) { async load(id: string) {
const [path, query] = id.split('?'); const [path, query] = id.split('?');
if (query != 'base64') return null; if (query != 'base64') return null;
+2 -1
View File
@@ -5,7 +5,7 @@ import tar from 'tar-stream';
export const macCommandLoader: Plugin = { export const macCommandLoader: Plugin = {
name: 'mac-command-loader', name: 'mac-command-loader',
async transform(_: unknown, id: string) { async load(id: string) {
const [path, query] = id.split('?'); const [path, query] = id.split('?');
if (!query || !query.includes('mac-command')) return null; if (!query || !query.includes('mac-command')) return null;
@@ -23,6 +23,7 @@ export const macCommandLoader: Plugin = {
const chunks: Buffer[] = []; const chunks: Buffer[] = [];
for await (const chunk of tarball) { for await (const chunk of tarball) {
if (!Buffer.isBuffer(chunk)) throw new TypeError('Expected a tarball Buffer chunk');
chunks.push(chunk); chunks.push(chunk);
} }
const dataBuffer = Buffer.concat(chunks); const dataBuffer = Buffer.concat(chunks);
+1 -2
View File
@@ -2,7 +2,7 @@
"compilerOptions": { "compilerOptions": {
"target": "ESNext", "target": "ESNext",
"lib": ["DOM", "DOM.Iterable", "ESNext"], "lib": ["DOM", "DOM.Iterable", "ESNext"],
"types": ["@types/wicg-file-system-access", "vitest/globals", "@testing-library/jest-dom"], "types": ["node", "@types/wicg-file-system-access", "vitest/globals", "@testing-library/jest-dom/vitest"],
"module": "ESNext", "module": "ESNext",
"skipLibCheck": true, "skipLibCheck": true,
/* Bundler mode */ /* Bundler mode */
@@ -17,7 +17,6 @@
"noUnusedLocals": true, "noUnusedLocals": true,
"noUnusedParameters": true, "noUnusedParameters": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"baseUrl": ".",
"esModuleInterop": true, "esModuleInterop": true,
"paths": { "paths": {
"~/*": ["./src/*"], "~/*": ["./src/*"],
+1
View File
@@ -4,6 +4,7 @@
"skipLibCheck": true, "skipLibCheck": true,
"module": "ESNext", "module": "ESNext",
"moduleResolution": "bundler", "moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"allowSyntheticDefaultImports": true "allowSyntheticDefaultImports": true
}, },
"include": ["vite.config.ts", "support/**/*.ts"] "include": ["vite.config.ts", "support/**/*.ts"]
+15 -17
View File
@@ -6,19 +6,18 @@ import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react'; import react from '@vitejs/plugin-react';
import wasm from 'vite-plugin-wasm'; import wasm from 'vite-plugin-wasm';
import replace from '@rollup/plugin-replace'; import replace from '@rollup/plugin-replace';
import topLevelAwait from 'vite-plugin-top-level-await';
import { VitePWA } from 'vite-plugin-pwa'; import { VitePWA } from 'vite-plugin-pwa';
import tailwindcss from '@tailwindcss/vite'; import tailwindcss from '@tailwindcss/vite';
import { tryCommand } from './support/command'; import { tryCommand } from './support/command.ts';
import { base64Loader } from './support/b64-loader'; import { base64Loader } from './support/b64-loader.ts';
import { macCommandLoader } from './support/mac-command-loader'; import { macCommandLoader } from './support/mac-command-loader.ts';
const projectRoot = url.fileURLToPath(new URL('.', import.meta.url)); const projectRoot = url.fileURLToPath(new URL('.', import.meta.url));
const pkg = JSON.parse(fs.readFileSync(projectRoot + '/package.json', 'utf-8')); const pkg = JSON.parse(fs.readFileSync(projectRoot + '/package.json', 'utf-8')) as { version: string };
const COMMAND_GIT_VERSION = 'git describe --long --dirty --tags --always'; const COMMAND_GIT_VERSION = 'git describe --long --dirty --tags --always';
const shortCommit = process.env.GIT_COMMIT || tryCommand(COMMAND_GIT_VERSION, __dirname, 'unknown'); const shortCommit = process.env.GIT_COMMIT || tryCommand(COMMAND_GIT_VERSION, projectRoot, 'unknown');
const version = `${pkg.version} (${shortCommit})`; const version = `${pkg.version} (${shortCommit})`;
// https://vitejs.dev/config/ // https://vitejs.dev/config/
@@ -59,7 +58,6 @@ export default defineConfig({
}), }),
react(), react(),
wasm(), wasm(),
topLevelAwait(),
VitePWA({ VitePWA({
registerType: 'prompt', registerType: 'prompt',
workbox: { workbox: {
@@ -91,22 +89,22 @@ export default defineConfig({
], ],
resolve: { resolve: {
alias: { alias: {
'~': path.resolve(__dirname, 'src'), '~': path.resolve(projectRoot, 'src'),
'@sql-wasm': path.resolve(__dirname, 'node_modules', 'sql.js', 'dist', 'sql-wasm.wasm'), '@sql-wasm': path.resolve(projectRoot, 'node_modules', 'sql.js', 'dist', 'sql-wasm.wasm'),
// workaround for vite, workbox (PWA) // workaround for vite, workbox (PWA)
module: path.resolve(__dirname, 'src', 'dummy.mjs'), module: path.resolve(projectRoot, 'src', 'dummy.mjs'),
}, },
}, },
build: { build: {
minify: true, minify: true,
rollupOptions: { rolldownOptions: {
output: { output: {
manualChunks: { manualChunks(id) {
core: ['react', 'react-dom'], if (/\/node_modules\/(react|react-dom)\//.test(id)) return 'core';
router: ['react-router'], if (/\/node_modules\/react-router\//.test(id)) return 'router';
store: ['react-redux', '@reduxjs/toolkit'], if (/\/node_modules\/(react-redux|@reduxjs\/toolkit)\//.test(id)) return 'store';
extras: ['react-dropzone', 'react-toastify'], if (/\/node_modules\/(react-dropzone|react-toastify)\//.test(id)) return 'extras';
}, },
}, },
}, },
@@ -119,7 +117,7 @@ export default defineConfig({
// workaround: sql.js is not ESModule friendly, yet... // workaround: sql.js is not ESModule friendly, yet...
deps: { deps: {
optimizer: { optimizer: {
web: { client: {
include: ['sql.js'], include: ['sql.js'],
}, },
}, },