mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-05 00:33:27 +08:00
perf(hooks): use direct require() instead of spawning child process
Invoke hook scripts directly via require() when they export a run(rawInput) function, eliminating one Node.js process spawn per hook invocation (~50-100ms). Includes path traversal guard, timeouts, error logging, PR review feedback, legacy hooks guard, normalized filePath, and restored findProjectRoot config detection with package manager support.
This commit is contained in:
@@ -52,7 +52,15 @@ async function main() {
|
||||
}
|
||||
|
||||
const pluginRoot = getPluginRoot();
|
||||
const scriptPath = path.join(pluginRoot, relScriptPath);
|
||||
const resolvedRoot = path.resolve(pluginRoot);
|
||||
const scriptPath = path.resolve(pluginRoot, relScriptPath);
|
||||
|
||||
// Prevent path traversal outside the plugin root
|
||||
if (!scriptPath.startsWith(resolvedRoot + path.sep)) {
|
||||
process.stderr.write(`[Hook] Path traversal rejected for ${hookId}: ${scriptPath}\n`);
|
||||
process.stdout.write(raw);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(scriptPath)) {
|
||||
process.stderr.write(`[Hook] Script not found for ${hookId}: ${scriptPath}\n`);
|
||||
@@ -60,11 +68,43 @@ async function main() {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Prefer direct require() when the hook exports a run(rawInput) function.
|
||||
// This eliminates one Node.js process spawn (~50-100ms savings per hook).
|
||||
//
|
||||
// SAFETY: Only require() hooks that export run(). Legacy hooks execute
|
||||
// side effects at module scope (stdin listeners, process.exit, main() calls)
|
||||
// which would interfere with the parent process or cause double execution.
|
||||
let hookModule;
|
||||
const src = fs.readFileSync(scriptPath, 'utf8');
|
||||
const hasRunExport = /\bmodule\.exports\b/.test(src) && /\brun\b/.test(src);
|
||||
|
||||
if (hasRunExport) {
|
||||
try {
|
||||
hookModule = require(scriptPath);
|
||||
} catch (requireErr) {
|
||||
process.stderr.write(`[Hook] require() failed for ${hookId}: ${requireErr.message}\n`);
|
||||
// Fall through to legacy spawnSync path
|
||||
}
|
||||
}
|
||||
|
||||
if (hookModule && typeof hookModule.run === 'function') {
|
||||
try {
|
||||
const output = hookModule.run(raw);
|
||||
if (output != null) process.stdout.write(output);
|
||||
} catch (runErr) {
|
||||
process.stderr.write(`[Hook] run() error for ${hookId}: ${runErr.message}\n`);
|
||||
process.stdout.write(raw);
|
||||
}
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Legacy path: spawn a child Node process for hooks without run() export
|
||||
const result = spawnSync('node', [scriptPath], {
|
||||
input: raw,
|
||||
encoding: 'utf8',
|
||||
env: process.env,
|
||||
cwd: process.cwd(),
|
||||
timeout: 30000
|
||||
});
|
||||
|
||||
if (result.stdout) process.stdout.write(result.stdout);
|
||||
|
||||
Reference in New Issue
Block a user