Files
his/openhis-ui-vue3/scripts/patch-deps.js
chenqi d6ce0f28cc chore(deps): 添加依赖补丁脚本解决 Vue 3 兼容性问题
- 创建 patch-deps.js 脚本用于修补 node_modules 中的依赖问题
- 修复 xe-utils hasOwnProp.js 的 Vue 3 Proxy 兼容性问题
- 为 element-plus form-label-wrap.mjs 添加 NaN 防护和生命周期守卫
- 实现幂等性确保可安全重复执行
- 添加自动跳过已修补文件的检查机制
2026-06-03 14:42:00 +08:00

138 lines
5.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* patch-deps.js — 统一修补 node_modules 依赖
*
* 解决 Vue 3 + Vite 项目中依赖兼容性问题。
* 每次 npm install 后由 postinstall 自动执行,也可手动运行:
* node scripts/patch-deps.js
*
* 修补清单:
* 1. xe-utils/hasOwnProp.js — Vue 3 Proxy 兼容
* 2. element-plus form-label-wrap.mjs — NaN 防护 + 生命周期守卫
*
* 特性幂等safe to run multiple times
*/
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.resolve(__dirname, '..');
function patchFile(filePath, marker, patcher) {
if (!fs.existsSync(filePath)) {
console.log(`[patch-deps] SKIP (not found): ${path.relative(ROOT, filePath)}`);
return false;
}
const code = fs.readFileSync(filePath, 'utf-8');
if (code.includes(marker)) {
console.log(`[patch-deps] OK (already patched): ${path.relative(ROOT, filePath)}`);
return false;
}
const patched = patcher(code);
if (patched === code) {
console.log(`[patch-deps] SKIP (no change): ${path.relative(ROOT, filePath)}`);
return false;
}
fs.writeFileSync(filePath, patched, 'utf-8');
console.log(`[patch-deps] PATCHED: ${path.relative(ROOT, filePath)}`);
return true;
}
// ──────────────────────────────────────────────
// 1. xe-utils/hasOwnProp.js — Vue 3 Proxy 兼容
// ──────────────────────────────────────────────
// 根因Object.prototype.hasOwnProperty.call(proxyObj, key) 在 Vue 3 reactive
// Proxy 上会触发 reactivity 拦截,抛出 "obj.hasOwnProperty is not a function"。
// 修复try-catch + key-in fallback。
// ──────────────────────────────────────────────
patchFile(
path.join(ROOT, 'node_modules/xe-utils/hasOwnProp.js'),
'[vue3-proxy-safe]',
() => `/**
* Check if object has own property - Vue 3 Proxy safe [vue3-proxy-safe]
*
* Patched by scripts/patch-deps.js — do not edit manually.
* Re-run: node scripts/patch-deps.js
*/
function hasOwnProp (obj, key) {
if (obj == null) return false
try {
return Object.prototype.hasOwnProperty.call(obj, key)
} catch (e) {
try {
return key in Object(obj)
} catch (e2) {
return false
}
}
}
module.exports = hasOwnProp
`
);
// ──────────────────────────────────────────────
// 2. element-plus form-label-wrap.mjs — NaN + 生命周期守卫
// ──────────────────────────────────────────────
// 根因vxe-table 展开行收起时 el-form 组件已卸载,但 nextTick/onUpdated
// 回调仍尝试访问已销毁的 formContext导致 NaN width 和各种 teardown 错误。
// 修复NaN 防护 + _isMounted 生命周期守卫 + try-catch。
// ──────────────────────────────────────────────
patchFile(
path.join(ROOT, 'node_modules/element-plus/es/components/form/src/form-label-wrap.mjs'),
'_isMounted',
(code) => {
let patched = code
// NaN 防护
.replace(
'return Math.ceil(Number.parseFloat(width))',
'return Math.ceil(Number.parseFloat(width)) || 0'
)
// 添加 _isMounted 守卫变量
.replace(
'const updateLabelWidth = (action = "update") => {',
'let _isMounted = true;\n\tconst updateLabelWidth = (action = "update") => {'
)
// nextTick 回调前置守卫
.replace(
'nextTick(() => {',
'nextTick(() => {\n\t\t\t\tif (!_isMounted) return;'
)
// try-catch 包裹核心逻辑
.replace(
'if (slots.default && props.isAutoWidth) {',
'try {\n\t\t\t\tif (slots.default && props.isAutoWidth) {'
)
.replace(
'else if (action === "remove") formContext?.deregisterLabelWidth(computedWidth.value);',
'else if (action === "remove") formContext?.deregisterLabelWidth(computedWidth.value);\n\t\t\t\t}\n\t\t\t} catch (e) { /* teardown race */ }'
)
// onBeforeUnmount 设置守卫
.replace(
'onBeforeUnmount(() => {',
'onBeforeUnmount(() => {\n\t\t\t_isMounted = false;'
)
// onUpdated 守卫
.replace(
'onUpdated(() => updateLabelWidthFn())',
'onUpdated(() => { if (_isMounted) updateLabelWidthFn(); })'
)
// watch 守卫
.replace(
'if (props.updateAll) formContext?.registerLabelWidth(val, oldVal);',
'if (_isMounted && props.updateAll) formContext?.registerLabelWidth(val, oldVal);'
)
// render 函数守卫
.replace(
'return () => {',
'return () => {\n\t\t\tif (!_isMounted) return null;'
);
return patched;
}
);
// ──────────────────────────────────────────────
// 完成
// ──────────────────────────────────────────────
console.log('[patch-deps] Done.');