|
| 1 | +const { execSync } = require('node:child_process') |
| 2 | +const fs = require('node:fs') |
| 3 | +const path = require('node:path') |
| 4 | + |
| 5 | +/* |
| 6 | + * 应用 Quill 补丁脚本 |
| 7 | + * 功能:修复 Quill 编辑器输入法状态下的批处理问题,提升中文输入体验 |
| 8 | + * |
| 9 | + * 自动应用:安装包时 postinstall 脚本自动应用,支持所有包管理器-受限于 postinstall 规则暂未实现 |
| 10 | + * |
| 11 | + * 手动应用:在项目根目录执行 |
| 12 | + * node node_modules/@opentiny/fluent-editor/scripts/apply-patches.cjs |
| 13 | + * |
| 14 | + * 工作原理: |
| 15 | + * 1. 检测包管理器类型(pnpm/npm/yarn) |
| 16 | + * 2. 自动检测 Quill 安装位置 |
| 17 | + * 3. 根据包管理器类型应用不同的补丁策略 |
| 18 | + * |
| 19 | + * 注意事项: |
| 20 | + * - 补丁是幂等的,多次运行无副作用 |
| 21 | + * - 需要 node_modules 写入权限 |
| 22 | + * - 不影响其他包或项目的补丁 |
| 23 | + */ |
| 24 | + |
| 25 | +// 获取包管理器 |
| 26 | +function detectPackageManager() { |
| 27 | + try { |
| 28 | + const lockFiles = { |
| 29 | + 'pnpm-lock.yaml': 'pnpm', |
| 30 | + 'yarn.lock': 'yarn', |
| 31 | + 'package-lock.json': 'npm', |
| 32 | + } |
| 33 | + |
| 34 | + for (const [file, manager] of Object.entries(lockFiles)) { |
| 35 | + if (fs.existsSync(file)) return manager |
| 36 | + } |
| 37 | + |
| 38 | + const userAgent = process.env.npm_config_user_agent || '' |
| 39 | + if (userAgent.includes('pnpm')) return 'pnpm' |
| 40 | + if (userAgent.includes('yarn')) return 'yarn' |
| 41 | + if (userAgent.includes('npm')) return 'npm' |
| 42 | + } |
| 43 | + catch { |
| 44 | + return 'npm' |
| 45 | + } |
| 46 | + |
| 47 | + return 'npm' |
| 48 | +} |
| 49 | + |
| 50 | +// 拷贝 patches 文件 |
| 51 | +function copyPatchFile() { |
| 52 | + const packageManager = detectPackageManager() |
| 53 | + // 把 patch 文件复制到哪里 |
| 54 | + const dest = `patches/${packageManager === 'pnpm' ? 'quill@2.0.3.patch' : 'quill+2.0.3.patch'}` |
| 55 | + |
| 56 | + // 文件存在直接返回 |
| 57 | + if (fs.existsSync(dest)) { |
| 58 | + return true |
| 59 | + } |
| 60 | + |
| 61 | + // 复制 patch 文件的路径 |
| 62 | + const src = path.resolve(__dirname, '../patches/quill@2.0.3.patch') |
| 63 | + |
| 64 | + if (fs.existsSync(src)) { |
| 65 | + fs.mkdirSync('patches', { recursive: true }) |
| 66 | + |
| 67 | + if (packageManager === 'pnpm') { |
| 68 | + fs.copyFileSync(src, dest) |
| 69 | + } |
| 70 | + else { |
| 71 | + let content = fs.readFileSync(src, 'utf-8') |
| 72 | + content = content.replaceAll('core/editor.js', 'node_modules/quill/core/editor.js') |
| 73 | + fs.writeFileSync(dest, content) |
| 74 | + } |
| 75 | + |
| 76 | + console.log(`✅ 已复制 patch 文件到 ${dest}`) |
| 77 | + return true |
| 78 | + } |
| 79 | + else { |
| 80 | + console.log(`⚠️ 未找到 patch 文件,请手动创建 ${dest}`) |
| 81 | + return false |
| 82 | + } |
| 83 | +} |
| 84 | + |
| 85 | +// pnpm 应用 patch 文件 |
| 86 | +function setupPnpmPatch() { |
| 87 | + try { |
| 88 | + const packageJsonPath = 'package.json' |
| 89 | + if (!fs.existsSync(packageJsonPath)) { |
| 90 | + console.log('⚠️ 未找到 package.json') |
| 91 | + return false |
| 92 | + } |
| 93 | + |
| 94 | + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')) |
| 95 | + |
| 96 | + // 检查是否已经有 pnpm.patchedDependencies 配置 |
| 97 | + if (packageJson.pnpm?.patchedDependencies?.['quill@2.0.3']) { |
| 98 | + console.log('✅ pnpm patchedDependencies 已配置') |
| 99 | + return true |
| 100 | + } |
| 101 | + |
| 102 | + // 添加 pnpm.patchedDependencies 配置 |
| 103 | + if (!packageJson.pnpm) { |
| 104 | + packageJson.pnpm = {} |
| 105 | + } |
| 106 | + |
| 107 | + if (!packageJson.pnpm.patchedDependencies) { |
| 108 | + packageJson.pnpm.patchedDependencies = {} |
| 109 | + } |
| 110 | + |
| 111 | + packageJson.pnpm.patchedDependencies['quill@2.0.3'] = 'patches/quill@2.0.3.patch' |
| 112 | + |
| 113 | + fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)) |
| 114 | + console.log('✅ 已添加 pnpm patchedDependencies 配置') |
| 115 | + |
| 116 | + try { |
| 117 | + execSync('pnpm install', { stdio: 'inherit' }) |
| 118 | + console.log('✅ pnpm patch quill@2.0.3 应用成功!') |
| 119 | + } |
| 120 | + catch (error) { |
| 121 | + console.warn('❌ pnpm patch 命令执行失败:', error.message) |
| 122 | + } |
| 123 | + |
| 124 | + return true |
| 125 | + } |
| 126 | + catch (error) { |
| 127 | + console.error('❌ pnpm 补丁配置失败:', error.message) |
| 128 | + return false |
| 129 | + } |
| 130 | +} |
| 131 | + |
| 132 | +// npm、yarn 应用 patch 文件 |
| 133 | +function applyPatchPackage() { |
| 134 | + try { |
| 135 | + const packageManager = detectPackageManager() |
| 136 | + |
| 137 | + // 检查 patch-package 是否安装 |
| 138 | + let patchPackageInstalled = false |
| 139 | + try { |
| 140 | + require.resolve('patch-package') |
| 141 | + patchPackageInstalled = true |
| 142 | + } |
| 143 | + catch (e) { |
| 144 | + // patch-package 未安装 |
| 145 | + } |
| 146 | + |
| 147 | + if (!patchPackageInstalled) { |
| 148 | + console.log('📦 正在安装 patch-package...') |
| 149 | + try { |
| 150 | + const installCommand = packageManager === 'yarn' ? 'yarn add --dev patch-package' : 'npm install --save-dev patch-package' |
| 151 | + execSync(installCommand, { stdio: 'inherit' }) |
| 152 | + console.log('✅ patch-package 安装成功') |
| 153 | + } |
| 154 | + catch (error) { |
| 155 | + console.error('❌ patch-package 安装失败:', error.message) |
| 156 | + return false |
| 157 | + } |
| 158 | + } |
| 159 | + |
| 160 | + // 应用补丁 |
| 161 | + console.log('🔄 正在应用 patch...') |
| 162 | + try { |
| 163 | + execSync('npx patch-package', { stdio: 'inherit' }) |
| 164 | + console.log('✅ 补丁应用成功') |
| 165 | + } |
| 166 | + catch (error) { |
| 167 | + console.error('❌ 补丁应用失败:', error.message) |
| 168 | + return false |
| 169 | + } |
| 170 | + |
| 171 | + // 添加 postinstall 脚本 |
| 172 | + const packageJsonPath = 'package.json' |
| 173 | + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')) |
| 174 | + |
| 175 | + if (!packageJson.scripts) { |
| 176 | + packageJson.scripts = {} |
| 177 | + } |
| 178 | + |
| 179 | + if (!packageJson.scripts.postinstall || !packageJson.scripts.postinstall.includes('patch-package')) { |
| 180 | + const existingPostinstall = packageJson.scripts.postinstall || '' |
| 181 | + packageJson.scripts.postinstall = existingPostinstall ? `${existingPostinstall} && npx patch-package` : 'npx patch-package' |
| 182 | + |
| 183 | + fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)) |
| 184 | + console.log('✅ 已更新 postinstall 脚本') |
| 185 | + } |
| 186 | + |
| 187 | + return true |
| 188 | + } |
| 189 | + catch (error) { |
| 190 | + console.error('❌ patch-package 应用失败:', error.message) |
| 191 | + return false |
| 192 | + } |
| 193 | +} |
| 194 | + |
| 195 | +function handlePatchFailure() { |
| 196 | + console.log(` |
| 197 | +❌ 补丁处理失败,请尝试手动安装 |
| 198 | +
|
| 199 | +🔧 手动安装: |
| 200 | + 在项目根目录执行: |
| 201 | + node node_modules/@opentiny/fluent-editor/scripts/apply-patches.cjs |
| 202 | +
|
| 203 | +⚠️ 注意:未应用补丁可能影响中文输入体验 |
| 204 | +`) |
| 205 | +} |
| 206 | + |
| 207 | +function applyQuillPatch() { |
| 208 | + const packageManager = detectPackageManager() |
| 209 | + console.log(`🔍 检测到包管理器: ${packageManager}`) |
| 210 | + console.log('📋 准备 patch 文件...') |
| 211 | + |
| 212 | + if (!copyPatchFile()) return handlePatchFailure() |
| 213 | + |
| 214 | + let strategy |
| 215 | + if (packageManager == 'pnpm') { |
| 216 | + strategy = { |
| 217 | + label: '📦 使用 pnpm 补丁策略...', |
| 218 | + handler: setupPnpmPatch, |
| 219 | + successMsg: '✅ quill@2.0.3.patch 补丁配置已完成', |
| 220 | + } |
| 221 | + } |
| 222 | + else { |
| 223 | + strategy = { |
| 224 | + label: '📦 使用 patch-package 补丁策略...', |
| 225 | + handler: applyPatchPackage, |
| 226 | + successMsg: '🎉 补丁处理完成', |
| 227 | + } |
| 228 | + } |
| 229 | + |
| 230 | + if (!strategy) { |
| 231 | + console.log('❌ 不支持的包管理器,仅支持 pnpm、yarn、npm') |
| 232 | + handlePatchFailure() |
| 233 | + return |
| 234 | + } |
| 235 | + |
| 236 | + console.log(strategy.label) |
| 237 | + // 调用处理函数 |
| 238 | + const success = strategy.handler() |
| 239 | + |
| 240 | + if (success) { |
| 241 | + console.log(strategy.successMsg) |
| 242 | + } |
| 243 | + else { |
| 244 | + handlePatchFailure() |
| 245 | + } |
| 246 | +} |
| 247 | + |
| 248 | +applyQuillPatch() |
0 commit comments