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:
runs-on: ubuntu-latest
env:
npm_config_registry: https://registry.npmmirror.com
pnpm_config_registry: https://registry.npmmirror.com
steps:
- name: Check out repository code
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/
+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 PATH="$PNPM_HOME:$PATH"
WORKDIR /app
@@ -9,7 +9,7 @@ RUN corepack enable pnpm \
&& apt-get clean \
&& 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
COPY . .
RUN pnpm install --frozen-lockfile
+6 -2
View File
@@ -4,8 +4,8 @@
## 安装依赖
- 安装 Node v16.17 或更高,推荐当前最新的 Node LTS 版本。
- 安装/激活 `pnpm` [^1]:`corepack prepare pnpm@latest --activate`
- 安装 Node 24.22.0;pnpm 也会根据 `package.json` 中的 `devEngines.runtime` 自动获取该版本。
- 安装/激活项目指定的 `pnpm` [^1]:`corepack prepare pnpm@12.14.0 --activate`
- 安装软件依赖:`pnpm i --frozen-lockfile`
[^1]: 参考 pnpm 说明「[使用 Corepack 安装](https://pnpm.io/zh/installation#使用-corepack-安装)」。
@@ -34,6 +34,10 @@ pnpm build
[vite-preview-url]: http://localhost:4173/
### TypeScript 工具链
编译和 ESLint 统一使用 TypeScript 6.x,暂不升级到 TypeScript 7。
## 打包 `.zip`
建议在 Linux 环境下执行,可参考 `.drone.yml` CI 文件。
+26 -34
View File
@@ -22,26 +22,26 @@
"classnames": "^2.5.1",
"fflate": "^0.8.3",
"idb-keyval": "^6.3.0",
"nanoid": "^5.1.16",
"nanoid": "^6.0.2",
"radash": "^12.1.1",
"react": "^19.3.1",
"react-dom": "^19.3.1",
"react-dropzone": "^14.4.1",
"react-dropzone": "^20.1.4",
"react-icons": "^5.7.0",
"react-redux": "^9.3.0",
"react-router": "^7.18.4",
"react-syntax-highlighter": "^15.6.6",
"react-router": "^8.5.0",
"react-syntax-highlighter": "^16.1.2",
"react-toastify": "^11.1.0",
"sql.js": "^1.15.1"
},
"devDependencies": {
"@eslint/js": "^9.39.5",
"@eslint/js": "^10.0.1",
"@rollup/plugin-replace": "^6.0.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/user-event": "^14.6.8",
"@types/node": "^24.19.2",
"@types/node": "^26.6.5",
"@types/react": "^19.3.0",
"@types/react-dom": "^19.3.0",
"@types/react-syntax-highlighter": "^15.5.13",
@@ -50,18 +50,18 @@
"@types/wicg-file-system-access": "^2023.10.7",
"@typescript-eslint/eslint-plugin": "^8.72.0",
"@typescript-eslint/parser": "^8.72.0",
"@vitejs/plugin-react": "^5.2.0",
"@vitest/coverage-v8": "^3.2.7",
"@vitest/ui": "^3.2.7",
"@vitejs/plugin-react": "^6.1.2",
"@vitest/coverage-v8": "^5.0.5",
"@vitest/ui": "^5.0.5",
"daisyui": "^5.7.50",
"eslint": "^9.39.5",
"eslint": "^10.13.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-refresh": "^0.4.26",
"globals": "^16.5.0",
"eslint-plugin-react-refresh": "^0.5.7",
"globals": "^17.13.0",
"husky": "^9.1.7",
"jsdom": "^27.4.0",
"lint-staged": "^16.4.0",
"jsdom": "^30.1.3",
"lint-staged": "^17.6.0",
"prettier": "^3.9.10",
"rollup": "^4.64.12",
"sass": "^1.105.2",
@@ -69,13 +69,12 @@
"tailwindcss": "^4.3.3",
"tar-stream": "^3.2.2",
"terser": "^5.51.2",
"typescript": "^5.9.3",
"typescript": "^6.0.3",
"typescript-eslint": "^8.72.0",
"vite": "^7.3.7",
"vite-plugin-pwa": "^1.3.0",
"vite-plugin-top-level-await": "^1.6.0",
"vite": "^8.3.4",
"vite-plugin-pwa": "^2.0.0",
"vite-plugin-wasm": "^3.6.0",
"vitest": "^3.2.7",
"vitest": "^5.0.5",
"workbox-build": "^7.4.1",
"workbox-window": "^7.4.1"
},
@@ -92,19 +91,12 @@
"printWidth": 120,
"tabWidth": 2
},
"pnpm": {
"patchedDependencies": {
"sql.js": "patches/sql.js.patch"
},
"overrides": {
"sourcemap-codec": "npm:@jridgewell/[email protected]"
},
"onlyBuiltDependencies": [
"@swc/core",
"@tailwindcss/oxide",
"esbuild",
"simple-git-hooks"
]
"devEngines": {
"runtime": {
"name": "node",
"version": "24.22.0",
"onFail": "download"
}
},
"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?
// see: https://github.com/developit/jsdom-worker
+1 -1
View File
@@ -17,7 +17,7 @@ export function renderWithProviders(
ui: React.ReactElement,
{ 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>;
}
+1 -1
View File
@@ -1,6 +1,6 @@
import { MMKVParser } from '../MMKVParser';
const makeViewFromBuffer = (buff: Buffer) =>
const makeViewFromBuffer = (buff: Buffer<ArrayBuffer>) =>
new DataView(buff.buffer.slice(buff.byteOffset, buff.byteOffset + buff.byteLength));
test('throw error on broken file', () => {
+1 -1
View File
@@ -1,7 +1,7 @@
import { readFileSync } from 'node:fs';
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));
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 { parseAndroidQmEKey } from '../qm';
const makeViewFromBuffer = (buff: Buffer) =>
const makeViewFromBuffer = (buff: Buffer<ArrayBuffer>) =>
new DataView(buff.buffer.slice(buff.byteOffset, buff.byteOffset + buff.byteLength));
test('parse qm mmkv file', () => {
+1 -1
View File
@@ -3,7 +3,7 @@ import { Plugin } from 'vite';
export const base64Loader: Plugin = {
name: 'base64-loader',
async transform(_: unknown, id: string) {
async load(id: string) {
const [path, query] = id.split('?');
if (query != 'base64') return null;
+2 -1
View File
@@ -5,7 +5,7 @@ import tar from 'tar-stream';
export const macCommandLoader: Plugin = {
name: 'mac-command-loader',
async transform(_: unknown, id: string) {
async load(id: string) {
const [path, query] = id.split('?');
if (!query || !query.includes('mac-command')) return null;
@@ -23,6 +23,7 @@ export const macCommandLoader: Plugin = {
const chunks: Buffer[] = [];
for await (const chunk of tarball) {
if (!Buffer.isBuffer(chunk)) throw new TypeError('Expected a tarball Buffer chunk');
chunks.push(chunk);
}
const dataBuffer = Buffer.concat(chunks);
+1 -2
View File
@@ -2,7 +2,7 @@
"compilerOptions": {
"target": "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",
"skipLibCheck": true,
/* Bundler mode */
@@ -17,7 +17,6 @@
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"baseUrl": ".",
"esModuleInterop": true,
"paths": {
"~/*": ["./src/*"],
+1
View File
@@ -4,6 +4,7 @@
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"allowSyntheticDefaultImports": true
},
"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 wasm from 'vite-plugin-wasm';
import replace from '@rollup/plugin-replace';
import topLevelAwait from 'vite-plugin-top-level-await';
import { VitePWA } from 'vite-plugin-pwa';
import tailwindcss from '@tailwindcss/vite';
import { tryCommand } from './support/command';
import { base64Loader } from './support/b64-loader';
import { macCommandLoader } from './support/mac-command-loader';
import { tryCommand } from './support/command.ts';
import { base64Loader } from './support/b64-loader.ts';
import { macCommandLoader } from './support/mac-command-loader.ts';
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 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})`;
// https://vitejs.dev/config/
@@ -59,7 +58,6 @@ export default defineConfig({
}),
react(),
wasm(),
topLevelAwait(),
VitePWA({
registerType: 'prompt',
workbox: {
@@ -91,22 +89,22 @@ export default defineConfig({
],
resolve: {
alias: {
'~': path.resolve(__dirname, 'src'),
'@sql-wasm': path.resolve(__dirname, 'node_modules', 'sql.js', 'dist', 'sql-wasm.wasm'),
'~': path.resolve(projectRoot, 'src'),
'@sql-wasm': path.resolve(projectRoot, 'node_modules', 'sql.js', 'dist', 'sql-wasm.wasm'),
// workaround for vite, workbox (PWA)
module: path.resolve(__dirname, 'src', 'dummy.mjs'),
module: path.resolve(projectRoot, 'src', 'dummy.mjs'),
},
},
build: {
minify: true,
rollupOptions: {
rolldownOptions: {
output: {
manualChunks: {
core: ['react', 'react-dom'],
router: ['react-router'],
store: ['react-redux', '@reduxjs/toolkit'],
extras: ['react-dropzone', 'react-toastify'],
manualChunks(id) {
if (/\/node_modules\/(react|react-dom)\//.test(id)) return 'core';
if (/\/node_modules\/react-router\//.test(id)) return 'router';
if (/\/node_modules\/(react-redux|@reduxjs\/toolkit)\//.test(id)) return 'store';
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...
deps: {
optimizer: {
web: {
client: {
include: ['sql.js'],
},
},