前端架构师-week6-完成项目初始化整个流程

目录

引入项目模版类型和标准安装逻辑开发

拷贝项目模版功能开发

项目模版安装依赖和启动命令

白名单命令检测功能开发

项目名称自动格式化功能开发

本章核心:ejs 动态渲染项目模版 

init 命令直接传入项目名称功能支持


引入项目模版类型和标准安装逻辑开发

  判断是否为自定义模版,通过改动 mongodb 数据库中的数据来进行区分。

const TYPE_PROJECT = 'project';
const TYPE_COMPONENT = 'component';

const TEMPLATE_TYPE_NORMAL = 'normal';
const TEMPLATE_TYPE_CUSTOM = 'custom';

  ......
  async installTemplate() {
    log.verbose('templateInfo', this.templateInfo);
    if (this.templateInfo) {
      if (!this.templateInfo.type) {
        this.templateInfo.type = TEMPLATE_TYPE_NORMAL;
      }
      if (this.templateInfo.type === TEMPLATE_TYPE_NORMAL) {
        // 标准安装
        await this.installNormalTemplate();
      } else if (this.templateInfo.type === TEMPLATE_TYPE_CUSTOM) {
        // 自定义安装
        await this.installCustomTemplate();
      } else {
        throw new Error('无法识别项目模板类型!');
      }
    } else {
      throw new Error('项目模板信息不存在!');
    }
  }
  ......

拷贝项目模版功能开发

  ......
  async installNormalTemplate() {
    log.verbose('templateNpm', this.templateNpm);
    // 拷贝模板代码至当前目录
    let spinner = spinnerStart('正在安装模板...');
    await sleep();
    try {
      const templatePath = path.resolve(this.templateNpm.cacheFilePath, 'template');
      const targetPath = process.cwd();
      fse.ensureDirSync(templatePath);  // 确保目录存在
      fse.ensureDirSync(targetPath);
      fse.copySync(templatePath, targetPath);  // 拷贝文件内容
    } catch (e) {
      throw e;
    } finally {
      spinner.stop(true);
      log.success('模板安装成功');
    }
    // 依赖安装
    await this.execCommand(installCommand, '依赖安装失败!');  // 下节讲具体实现
    // 启动命令执行
    await this.execCommand(startCommand, '启动执行命令失败!');
  }
  ......

项目模版安装依赖和启动命令

//   mongodb 数据库中的数据增加两项

{
  "installCommand": "npm install",
  "startCommand": "npm run serve"
}
// 工具函数

function exec(command, args, options) {
  const win32 = process.platform === 'win32';

  const cmd = win32 ? 'cmd' : command;
  const cmdArgs = win32 ? ['/c'].concat(command, args) : args;

  return require('child_process').spawn(cmd, cmdArgs, options || {});
}

function execAsync(command, args, options) {
  return new Promise((resolve, reject) => {
    const p = exec(command, args, options);
    p.on('error', e => {
      reject(e);
    });
    p.on('exit', c => {
      resolve(c);
    });
  });
}
  ......
  async execCommand(command, errMsg) {
    let ret;
    if (command) {
      const cmdArray = command.split(' ');
      const cmd = this.checkCommand(cmdArray[0]);  // 下节讲具体实现
      if (!cmd) {
        throw new Error('命令不存在!命令:' + command);
      }
      const args = cmdArray.slice(1);
      ret = await execAsync(cmd, args, {
        stdio: 'inherit',                // 子进程实现,主进程中打印输出。
        cwd: process.cwd(),
      });
    }
    if (ret !== 0) {
      throw new Error(errMsg);
    }
    return ret;
  }
  ......

白名单命令检测功能开发

const WHITE_COMMAND = ['npm', 'cnpm'];

  ......
  checkCommand(cmd) {
    if (WHITE_COMMAND.includes(cmd)) {
      return cmd;
    }
    return null;
  }
  ......

项目名称自动格式化功能开发

// 修改项目模版的 package.json
{
  "name": "<%= className %>",
  "version": "<%= version %>"
}
// npm不能识别 <%= version %>,因此需要把模版放到 template 目录中,外层还包裹一个包。
# kebab-case 可以方便的将 驼峰 转换为 - 形式
# commands/init/ 命令行中执行
npm install -S kebab-case
    // 部分代码

    // 生成classname
    if (projectInfo.projectName) {
      projectInfo.name = projectInfo.projectName;
      projectInfo.className = require('kebab-case')(projectInfo.projectName).replace(/^-/, '');
    }

本章核心:ejs 动态渲染项目模版 

  ......
  async installNormalTemplate() {

    // 拷贝模板代码至当前目录

    // 模版拷贝完成后,npm install以前。
    const templateIgnore = this.templateInfo.ignore || [];
    // ignore 中的 public 不适合在这里写死,移到 mongodb 中更恰当。
    const ignore = ['**/node_modules/**', ...templateIgnore];
    await this.ejsRender({ ignore });

    // 依赖安装
    // 启动命令执行
  }
  ......
  ......
  async ejsRender(options) {
    const dir = process.cwd();
    const projectInfo = this.projectInfo;
    return new Promise((resolve, reject) => {
      glob('**', {
        cwd: dir,
        ignore: options.ignore || '',
        nodir: true,
      }, function(err, files) {
        if (err) {
          reject(err);
        }
        Promise.all(files.map(file => {
          const filePath = path.join(dir, file);
          return new Promise((resolve1, reject1) => {
            ejs.renderFile(filePath, projectInfo, {}, (err, result) => {
              if (err) {
                reject1(err);
              } else {
                // ejs.renderFile() 完成渲染,并没有写入。要把渲染结果重新写入。
                fse.writeFileSync(filePath, result);
                resolve1(result);
              }
            });
          });
        })).then(() => {
          resolve();
        }).catch(err => {
          reject(err);
        });
      });
    });
  }
  ......

init 命令直接传入项目名称功能支持

    // 部分代码    
    ......    
    let projectInfo = {};
    let isProjectNameValid = false;
    if (isValidName(this.projectName)) {  // this.projectName为命令行中项目名
      isProjectNameValid = true;
      projectInfo.projectName = this.projectName;
    }
    ......
    if (!isProjectNameValid) {
      projectPrompt.push(projectNamePrompt);
    }
    ......
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chengbo_eva

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值