@lark-project/cli
Version:
飞书项目插件开发工具
123 lines (105 loc) • 3.36 kB
JavaScript
/**
* preuninstall 钩子:卸载 CLI 时同步移除已注册的 skills。
*
* 解析 `skills` 可执行文件的优先级(无网络最稳 → 最兜底):
* 1. optionalDependency 拉到的 node_modules/skills/bin
* 2. 全局 PATH 上的 `skills` 命令
* 3. `npx --yes skills`(让 npx 自己读用户的 npm 配置,不强制覆盖)
*
* 卸载阶段任何失败都静默,不阻断卸载,不给用户添乱。
*/
;
const { spawn, execSync } = require('child_process');
const path = require('path');
const fs = require('fs');
const TIMEOUT_MS = 30_000;
function shouldSkip() {
if (process.env.LPM_SKIP_SKILLS) return 'LPM_SKIP_SKILLS 已设置';
if (process.env.CI) return 'CI 环境';
if (process.env.npm_config_ignore_scripts === 'true') return '--ignore-scripts';
const initCwd = process.env.INIT_CWD || process.cwd();
const pkgRoot = path.resolve(__dirname, '..');
if (initCwd === pkgRoot) return 'dev install(CLI 自身 yarn install)';
return null;
}
function resolveBundledSkills() {
try {
const pkgJsonPath = require.resolve('skills/package.json', {
paths: [path.resolve(__dirname, '..')],
});
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));
const binEntry = pkg.bin;
if (!binEntry) return null;
const relBin = typeof binEntry === 'string' ? binEntry : binEntry.skills;
if (!relBin) return null;
const absBin = path.resolve(path.dirname(pkgJsonPath), relBin);
return fs.existsSync(absBin) ? absBin : null;
} catch {
return null;
}
}
function resolveGlobalSkills() {
try {
const cmd = process.platform === 'win32' ? 'where skills' : 'command -v skills';
const out = execSync(cmd, { stdio: ['ignore', 'pipe', 'ignore'] })
.toString()
.trim()
.split(/\r?\n/)[0];
return out || null;
} catch {
return null;
}
}
function main() {
if (shouldSkip()) return;
const skillsDir = path.resolve(__dirname, '..', 'skills');
if (!fs.existsSync(skillsDir)) return;
console.log('→ unregistering Meegle plugin skills...');
const bundled = resolveBundledSkills();
if (bundled) return runRemove(process.execPath, [bundled, 'remove', skillsDir]);
const globalBin = resolveGlobalSkills();
if (globalBin) return runRemove(globalBin, ['remove', skillsDir]);
return runRemove('npx', ['--yes', 'skills', 'remove', skillsDir]);
}
function runRemove(cmd, args) {
const child = spawn(cmd, args, {
stdio: 'inherit',
shell: process.platform === 'win32',
detached: process.platform !== 'win32',
});
const timer = setTimeout(() => killTree(child), TIMEOUT_MS);
child.on('error', () => clearTimeout(timer));
child.on('exit', code => {
clearTimeout(timer);
try {
if (code === 0) console.log('✓ Meegle plugin skills unregistered');
} catch {
/* EPIPE 兜底 */
}
});
}
function killTree(child) {
if (!child || !child.pid) return;
try {
if (process.platform === 'win32') {
child.kill();
} else {
process.kill(-child.pid, 'SIGTERM');
setTimeout(() => {
try {
process.kill(-child.pid, 'SIGKILL');
} catch {
/* exited */
}
}, 2000).unref();
}
} catch {
/* exited */
}
}
try {
main();
} catch {
/* silent on uninstall */
}