You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular Schematics Tree修改未持久化致npm命令执行失败问题排查

问题分析

你遇到的核心问题是Angular Schematics的虚拟文件系统与磁盘文件不同步:

  • 调用tree.overwrite()只是修改了内存中的package.json,并没有同步到磁盘
  • execSync()执行npm命令时,读取的是磁盘上的原始文件,此时你添加的prepare脚本还不存在,所以报错
修复方案

1. 先将修改后的package.json写入磁盘

在调用execSync之前,需要把内存中修改好的package.json同步到磁盘。可以直接用Node.js的fs模块完成。

2. 完善代码细节

  • 检查devDependencies字段是否存在,避免直接赋值报错
  • 处理prepare脚本已存在的情况(可选)
  • 添加错误捕获,方便调试
  • 确保husky命令执行环境正确

修改后的完整代码:

import {
    type Rule,
    type SchematicContext,
    type Tree,
} from '@angular-devkit/schematics';
import {normalize} from '@angular-devkit/core';
import {execSync} from 'child_process';
import * as fs from 'fs';
import * as path from 'path';

export function main(options: {directory: string}): Rule {
    return (tree: Tree, context: SchematicContext) => {
        context.logger.info('Adding husky ...');
        const dirPath = normalize(options.directory);
        return updatePackageJson(dirPath)(tree, context);
    };
}

function updatePackageJson(directory: string): Rule {
    return (tree: Tree, context: SchematicContext) => {
        const pkgPath = path.join(directory, 'package.json');
        const buffer = tree.read(pkgPath);
        
        if (!buffer) {
            throw new Error(`Package.json not found at ${pkgPath}`);
        }

        // 解析并修改package.json
        const pkgContent = buffer.toString();
        const parsedPackage = JSON.parse(pkgContent);
        
        // 确保devDependencies存在
        if (!parsedPackage.devDependencies) {
            parsedPackage.devDependencies = {};
        }
        parsedPackage.devDependencies.husky = '^8.0.1';
        
        // 处理prepare脚本:如果已存在可以选择提示或覆盖
        if (parsedPackage.scripts?.prepare) {
            context.logger.warn('Existing prepare script found, will be overwritten');
        }
        parsedPackage.scripts = {...parsedPackage.scripts, prepare: 'husky install'};

        // 1. 先写入内存树
        const updatedPkgContent = JSON.stringify(parsedPackage, null, 2);
        tree.overwrite(pkgPath, updatedPkgContent);
        
        // 2. 同步到磁盘(关键:让npm能读取到修改后的文件)
        fs.writeFileSync(pkgPath, updatedPkgContent);

        try {
            // 执行npm安装和husky初始化
            context.logger.info('Installing husky...');
            execSync(`npm install --prefix=${directory}`, {stdio: 'inherit'});
            
            // husky 8+安装后prepare脚本会自动执行,这里可以省略手动调用,不过如果需要确保执行也可以保留
            context.logger.info('Running prepare script...');
            execSync(`npm run prepare --prefix=${directory}`, {stdio: 'inherit'});

            // 添加commit-msg钩子
            const commitMsgHookPath = path.join(directory, '.husky', 'commit-msg');
            context.logger.info('Adding commit-msg hook...');
            execSync(`npx husky add ${commitMsgHookPath} 'npx --no -- commitlint --edit "$1"'`, {
                cwd: directory,
                stdio: 'inherit'
            });

            // 添加pre-push钩子
            const prePushHookPath = path.join(directory, '.husky', 'pre-push');
            context.logger.info('Adding pre-push hook...');
            execSync(`npx husky add ${prePushHookPath} 'npm run test'`, {
                cwd: directory,
                stdio: 'inherit'
            });

            context.logger.info('Husky setup completed successfully!');
        } catch (error) {
            context.logger.error(`Husky setup failed: ${(error as Error).message}`);
            throw error;
        }

        return tree;
    };
}

关键说明

  • 同步磁盘文件:通过fs.writeFileSync将修改后的package.json写入磁盘,确保npm命令能读取到最新内容
  • stdio: 'inherit':让execSync执行的命令输出直接显示在终端,方便调试
  • cwd: directory:执行husky命令时设置工作目录,避免路径问题
  • husky自动执行prepare:husky 8.x版本在npm install后会自动触发prepare脚本,所以手动调用npm run prepare不是必须的,但保留也没问题
额外提示
  • 如果你的原理图需要发布给其他开发者使用,建议添加对commitlint的依赖检查(比如提示用户安装@commitlint/cli和@commitlint/config-conventional)
  • 可以通过Schematics的packageJson工具函数来更安全地修改package.json,比如@schematics/angular中的addPackageJsonDependency方法

内容的提问来源于stack exchange,提问作者David Miguel Yusta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 12:30:53