From 1d9b6099bd7ce72d0b656d45936fc81cd8d0ee68 Mon Sep 17 00:00:00 2001 From: Innei Date: Thu, 30 Apr 2026 17:56:22 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=91=B7=20build(repo):=20migrate=20to=20pn?= =?UTF-8?q?pm=20v11=20and=20consolidate=20workspace=20config=20(#14316)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 👷 build(repo): migrate to pnpm v11 and consolidate workspace config Made-with: Cursor * 👷 fix pnpm v11 install config --- .../actions/desktop-build-setup/action.yml | 11 --- .github/workflows/test.yml | 2 +- .npmrc | 21 ------ apps/cli/.npmrc | 14 ---- apps/cli/pnpm-workspace.yaml | 15 ++++ apps/desktop/.npmrc | 20 ------ apps/desktop/package.json | 12 ---- apps/desktop/pnpm-workspace.yaml | 31 ++++++++ package.json | 22 +----- patches/@upstash__qstash.patch | 12 ++-- pnpm-workspace.yaml | 68 +++++++++++++++--- src/libs/next/config/define-config.ts | 71 ++++++++++++++++++- vite.config.ts | 33 +++++++++ 13 files changed, 217 insertions(+), 115 deletions(-) delete mode 100644 .npmrc delete mode 100644 apps/cli/.npmrc delete mode 100644 apps/desktop/.npmrc diff --git a/.github/actions/desktop-build-setup/action.yml b/.github/actions/desktop-build-setup/action.yml index 30827aef95..4f0e1db5f5 100644 --- a/.github/actions/desktop-build-setup/action.yml +++ b/.github/actions/desktop-build-setup/action.yml @@ -18,17 +18,6 @@ runs: shell: bash run: pnpm install --node-linker=hoisted - # 移除国内 electron 镜像配置,GitHub Actions 使用官方源更快 - - name: Remove China electron mirror from .npmrc - shell: bash - run: | - NPMRC_FILE="./apps/desktop/.npmrc" - if [ -f "$NPMRC_FILE" ]; then - sed -i.bak '/^electron_mirror=/d; /^electron_builder_binaries_mirror=/d' "$NPMRC_FILE" - rm -f "${NPMRC_FILE}.bak" - echo "✅ Removed electron mirror config from .npmrc" - fi - - name: Install deps on Desktop shell: bash run: npm run install-isolated --prefix=./apps/desktop diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 55baf26eb0..cdc949a942 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -213,7 +213,7 @@ jobs: uses: ./.github/actions/setup-env - name: Install deps - run: pnpm i + run: pnpm i --config.enable-global-virtual-store=false - name: Lint run: bun run lint diff --git a/.npmrc b/.npmrc deleted file mode 100644 index 999be96503..0000000000 --- a/.npmrc +++ /dev/null @@ -1,21 +0,0 @@ -lockfile=false -resolution-mode=highest -dedupe-peer-dependents=true - -ignore-workspace-root-check=true -enable-pre-post-scripts=true - - -public-hoist-pattern[]=*@umijs/lint* -public-hoist-pattern[]=*changelog* -public-hoist-pattern[]=*commitlint* -public-hoist-pattern[]=*eslint* -public-hoist-pattern[]=*postcss* -public-hoist-pattern[]=*prettier* -public-hoist-pattern[]=*remark* -public-hoist-pattern[]=*semantic-release* -public-hoist-pattern[]=*stylelint* - -public-hoist-pattern[]=@auth/core -public-hoist-pattern[]=pdfjs-dist -public-hoist-pattern[]=@napi-rs/canvas-* diff --git a/apps/cli/.npmrc b/apps/cli/.npmrc deleted file mode 100644 index 6cc5e46826..0000000000 --- a/apps/cli/.npmrc +++ /dev/null @@ -1,14 +0,0 @@ -lockfile=false -ignore-workspace-root-check=true - -public-hoist-pattern[]=*@umijs/lint* -public-hoist-pattern[]=*unicorn* -public-hoist-pattern[]=*changelog* -public-hoist-pattern[]=*commitlint* -public-hoist-pattern[]=*eslint* -public-hoist-pattern[]=*postcss* -public-hoist-pattern[]=*prettier* -public-hoist-pattern[]=*remark* -public-hoist-pattern[]=*semantic-release* -public-hoist-pattern[]=*stylelint* - diff --git a/apps/cli/pnpm-workspace.yaml b/apps/cli/pnpm-workspace.yaml index 0ee104d019..1603bbae2c 100644 --- a/apps/cli/pnpm-workspace.yaml +++ b/apps/cli/pnpm-workspace.yaml @@ -4,3 +4,18 @@ packages: - '../../packages/local-file-shell' - '../../packages/file-loaders' - '.' + +lockfile: false +ignoreWorkspaceRootCheck: true + +publicHoistPattern: + - '*@umijs/lint*' + - '*unicorn*' + - '*changelog*' + - '*commitlint*' + - '*eslint*' + - '*postcss*' + - '*prettier*' + - '*remark*' + - '*semantic-release*' + - '*stylelint*' diff --git a/apps/desktop/.npmrc b/apps/desktop/.npmrc deleted file mode 100644 index 6245cc6527..0000000000 --- a/apps/desktop/.npmrc +++ /dev/null @@ -1,20 +0,0 @@ -lockfile=false -shamefully-hoist=true -ignore-workspace-root-check=true - -electron_mirror=https://npmmirror.com/mirrors/electron/ -electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/ - -public-hoist-pattern[]=*@umijs/lint* -public-hoist-pattern[]=*unicorn* -public-hoist-pattern[]=*changelog* -public-hoist-pattern[]=*commitlint* -public-hoist-pattern[]=*eslint* -public-hoist-pattern[]=*postcss* -public-hoist-pattern[]=*prettier* -public-hoist-pattern[]=*remark* -public-hoist-pattern[]=*semantic-release* -public-hoist-pattern[]=*stylelint* - -public-hoist-pattern[]=@auth/core -public-hoist-pattern[]=pdfjs-dist diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 2033cf82d4..a2bfc26257 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -114,17 +114,5 @@ }, "optionalDependencies": { "node-mac-permissions": "^2.5.0" - }, - "pnpm": { - "onlyBuiltDependencies": [ - "@napi-rs/canvas", - "electron", - "electron-builder", - "node-mac-permissions" - ], - "overrides": { - "react": "19.2.4", - "react-dom": "19.2.4" - } } } diff --git a/apps/desktop/pnpm-workspace.yaml b/apps/desktop/pnpm-workspace.yaml index d63bfb41b5..f5107810fa 100644 --- a/apps/desktop/pnpm-workspace.yaml +++ b/apps/desktop/pnpm-workspace.yaml @@ -11,3 +11,34 @@ packages: - './stubs/business-const' - './stubs/types' - '.' + +lockfile: false +shamefullyHoist: true +ignoreWorkspaceRootCheck: true + +publicHoistPattern: + - '*@umijs/lint*' + - '*unicorn*' + - '*changelog*' + - '*commitlint*' + - '*eslint*' + - '*postcss*' + - '*prettier*' + - '*remark*' + - '*semantic-release*' + - '*stylelint*' + - '@auth/core' + - 'pdfjs-dist' + +allowBuilds: + '@napi-rs/canvas': true + 'electron': true + 'electron-builder': true + 'electron-winstaller': true + 'esbuild': true + 'get-windows': true + 'node-mac-permissions': true + +overrides: + 'react': 19.2.4 + 'react-dom': 19.2.4 diff --git a/package.json b/package.json index 808028bc48..0264acd039 100644 --- a/package.json +++ b/package.json @@ -536,29 +536,9 @@ "vite-tsconfig-paths": "^6.1.1", "vitest": "^3.2.4" }, - "packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319", + "packageManager": "pnpm@11.0.0+sha512.5bd187500e49cc6c3d891d973b432c02b844a5eb7209172c90a517a3ef4f579ed5c23d409b699e6a9dc418ff7b2b1890e63f6d74f1d3fc49848f37779c89c84c", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" - }, - "pnpm": { - "onlyBuiltDependencies": [ - "@lobehub/editor", - "ffmpeg-static" - ], - "overrides": { - "@react-pdf/image": "3.0.4", - "@types/react": "19.2.13", - "better-auth": "1.4.6", - "better-call": "1.1.8", - "drizzle-orm": "^0.45.1", - "fast-xml-parser": "5.4.2", - "lexical": "0.42.0", - "pdfjs-dist": "5.4.530", - "stylelint-config-clean-order": "7.0.0" - }, - "patchedDependencies": { - "@upstash/qstash": "patches/@upstash__qstash.patch" - } } } diff --git a/patches/@upstash__qstash.patch b/patches/@upstash__qstash.patch index b64d7ad866..7950fcb9e0 100644 --- a/patches/@upstash__qstash.patch +++ b/patches/@upstash__qstash.patch @@ -1,8 +1,8 @@ -diff --git a/chunk-RQPZUJXG.mjs b/chunk-RQPZUJXG.mjs -index d1a9b4a460efc59304ec30e6bc63a127f1aac6d6..4303089796e63297f79926fc9f9bd976029b1a8e 100644 ---- a/chunk-RQPZUJXG.mjs -+++ b/chunk-RQPZUJXG.mjs -@@ -326,6 +326,20 @@ var HttpClient = class { +diff --git a/chunk-35B33QW3.mjs b/chunk-35B33QW3.mjs +index 06da5e4fb496772eceb471b3b51135e3e0c3574b..6b21ab7a4f3c3bb23b0c678f279f62496be81762 100644 +--- a/chunk-35B33QW3.mjs ++++ b/chunk-35B33QW3.mjs +@@ -1019,6 +1019,20 @@ var HttpClient = class { } if (response.status < 200 || response.status >= 300) { const body = await response.text(); @@ -23,7 +23,7 @@ index d1a9b4a460efc59304ec30e6bc63a127f1aac6d6..4303089796e63297f79926fc9f9bd976 throw new QstashError( body.length > 0 ? body : `Error: status=${response.status}`, response.status -@@ -1841,8 +1855,10 @@ var AutoExecutor = class _AutoExecutor { +@@ -2264,8 +2278,10 @@ var AutoExecutor = class _AutoExecutor { if (error instanceof QStashWorkflowAbort) { throw error; } diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 73431d841b..b6dbbacf73 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -4,17 +4,69 @@ packages: - e2e - apps/desktop/src/main -onlyBuiltDependencies: - - '@vercel/speed-insights' - - '@lobehub/editor' +lockfile: false +resolutionMode: highest +dedupePeerDependents: true +ignoreWorkspaceRootCheck: true +enablePrePostScripts: true +enableGlobalVirtualStore: true + +publicHoistPattern: + - '*@umijs/lint*' + - '*changelog*' + - '*commitlint*' + - '*eslint*' + - '*postcss*' + - '*prettier*' + - '*remark*' + - '*semantic-release*' + - '*stylelint*' + - '@auth/core' + - 'pdfjs-dist' + - '@napi-rs/canvas-*' + +packageExtensions: + '@ant-design/pro-descriptions': + dependencies: + '@ant-design/icons': ^6.2.1 + '@azure-rest/ai-inference': + dependencies: + '@azure/core-util': ^1.13.1 + '@cucumber/cucumber': + dependencies: + tsx: ^4.21.0 + rc-util: + dependencies: + react: 19.2.4 + stylelint: + dependencies: + postcss-styled-syntax: ^0.7.1 + +allowBuilds: + '@lobehub/editor': true + '@vercel/speed-insights': true + core-js: true + electron: true + es5-ext: true + esbuild: true + 'ffmpeg-static': true + protobufjs: true + sharp: true + unrs-resolver: true overrides: - jose: ^6.1.3 - stylelint-config-clean-order: 7.0.0 - pdfjs-dist: 5.4.530 - react: 19.2.4 - react-dom: 19.2.4 '@react-pdf/image': 3.0.4 + '@types/react': 19.2.13 + 'better-auth': 1.4.6 + 'better-call': 1.1.8 + 'drizzle-orm': ^0.45.1 + 'fast-xml-parser': 5.4.2 + 'jose': ^6.1.3 + 'lexical': 0.42.0 + 'pdfjs-dist': 5.4.530 + 'react': 19.2.4 + 'react-dom': 19.2.4 + 'stylelint-config-clean-order': 7.0.0 patchedDependencies: '@upstash/qstash': patches/@upstash__qstash.patch diff --git a/src/libs/next/config/define-config.ts b/src/libs/next/config/define-config.ts index 413438f728..d42ef95fa3 100644 --- a/src/libs/next/config/define-config.ts +++ b/src/libs/next/config/define-config.ts @@ -1,7 +1,67 @@ +import { existsSync, realpathSync } from 'node:fs'; +import { createRequire } from 'node:module'; +import { dirname, relative, resolve, sep } from 'node:path'; + import { codeInspectorPlugin } from 'code-inspector-plugin'; import { type NextConfig } from 'next'; import { type Header, type Redirect } from 'next/dist/lib/load-custom-routes'; +const require = createRequire(import.meta.url); + +const getPathSegments = (value: string) => + resolve(value) + .split(/[\\/]+/) + .filter(Boolean); + +const getCommonDirectory = (paths: string[]) => { + const [firstPath = [], ...remainingPaths] = paths.map(getPathSegments); + const commonSegments: string[] = []; + + for (const [index, segment] of firstPath.entries()) { + if (remainingPaths.every((pathSegments) => pathSegments[index] === segment)) { + commonSegments.push(segment); + continue; + } + + break; + } + + return commonSegments.length > 0 ? resolve(sep, ...commonSegments) : process.cwd(); +}; + +const getTurbopackRoot = () => { + const nextPackageDirectory = dirname(require.resolve('next/package.json')); + + return getCommonDirectory([realpathSync(process.cwd()), realpathSync(nextPackageDirectory)]); +}; + +const resolvePackageDirectory = (packageName: string) => { + const candidateDirectories = [ + resolve(process.cwd(), 'node_modules', packageName), + resolve(process.cwd(), 'node_modules/.pnpm/node_modules', packageName), + ]; + + return candidateDirectories.find((directory) => existsSync(resolve(directory, 'package.json'))); +}; + +const toTurbopackAliasPath = (directory: string) => + `./${relative(process.cwd(), directory).replaceAll(sep, '/')}`; + +const createTurbopackPeerAliases = () => + Object.fromEntries( + [ + '@azure/core-util', + '@opentelemetry/context-async-hooks', + 'drizzle-orm', + 'vscode-jsonrpc', + 'vscode-languageserver-types', + ].flatMap((packageName) => { + const packageDirectory = resolvePackageDirectory(packageName); + + return packageDirectory ? [[packageName, toTurbopackAliasPath(packageDirectory)]] : []; + }), + ); + interface CustomNextConfig { experimental?: NextConfig['experimental']; headers?: Header[]; @@ -15,6 +75,9 @@ interface CustomNextConfig { export function defineConfig(config: CustomNextConfig) { const isProd = process.env.NODE_ENV === 'production'; const buildWithDocker = process.env.DOCKER === 'true'; + const { resolveAlias: customTurbopackResolveAlias, ...customTurbopackConfig } = + config.turbopack ?? {}; + const turbopackRoot = getTurbopackRoot(); const shouldUseCSP = process.env.ENABLED_CSP === '1'; @@ -365,6 +428,12 @@ export function defineConfig(config: CustomNextConfig) { transpilePackages: ['mermaid', 'better-auth-harmony'], turbopack: { + ...customTurbopackConfig, + resolveAlias: { + ...createTurbopackPeerAliases(), + ...customTurbopackResolveAlias, + }, + root: turbopackRoot, rules: { ...(isTest ? void 0 @@ -376,8 +445,8 @@ export function defineConfig(config: CustomNextConfig) { as: '*.js', loaders: ['raw-loader'], }, + ...customTurbopackConfig.rules, }, - ...config.turbopack, }, typescript: { diff --git a/vite.config.ts b/vite.config.ts index eb83f70663..7aa68efe07 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,5 +1,6 @@ import { spawn } from 'node:child_process'; import fs from 'node:fs'; +import { builtinModules, createRequire } from 'node:module'; import path from 'node:path'; import type { PluginOption, ViteDevServer } from 'vite'; @@ -22,6 +23,37 @@ Object.assign(process.env, loadEnv(mode, process.cwd(), '')); const isDev = process.env.NODE_ENV !== 'production'; const platform = isMobile ? 'mobile' : 'web'; +const require = createRequire(import.meta.url); + +const isBareModuleId = (id: string) => + !id.startsWith('.') && !id.startsWith('/') && !id.includes('\0'); +const nodeBuiltinModules = new Set([ + ...builtinModules, + ...builtinModules.map((id) => `node:${id}`), +]); + +const globalVirtualStorePeerFallback = (): PluginOption => { + const fallbackPaths = [ + path.resolve(__dirname, 'node_modules'), + path.resolve(__dirname, 'node_modules/.pnpm/node_modules'), + ]; + + return { + enforce: 'post', + name: 'global-virtual-store-peer-fallback', + resolveId(source, importer) { + if (!importer || !isBareModuleId(source)) return; + if (nodeBuiltinModules.has(source)) return; + if (!importer.includes('/v11/links/')) return; + + try { + return require.resolve(source, { paths: fallbackPaths }); + } catch { + return; + } + }, + }; +}; const resolveCommandExecutable = (cmd: string) => { const pathValue = process.env.PATH; @@ -116,6 +148,7 @@ export default defineConfig({ }, optimizeDeps: sharedOptimizeDeps, plugins: [ + globalVirtualStorePeerFallback(), vercelSkewProtection(), viteEnvRestartKeys(['APP_URL']), ...sharedRendererPlugins({ platform }),