《vite技术揭秘、还原与实战》第2节--揭秘yarn create vite

前言

vite源码中,一共有三个工程,其中create-vite是一个辅助工具,用于快速创建模板工程,本小节,一起来看下它是怎么实现的

更新进度

公众号:更新至第5

博客:更新至第2

源码获取

传送门

源码分析

在开始分析源码前,有一个前置知识点需要进行下简要补充,即npm initnpm createnpm exec都是什么

1-npm init等价于npm create,它们的完整写法为npm init <initializer>

2-npm init最终会以npm exec create-<initializer>的形式调用npm exec

3-npm exec的执行流程

首先,从本地查找是否存在与create-<initializer>同名的包

如果找到包,则判断package.json文件中设置的bin字段,当为多入口时,尝试在key中查找与包名同名的key值并调用key对应的value值指向的文件,否则对bin字段指向的文件直接进行调用

如果找不到包,则尝试从远程查找与create-<initializer>同名的包并下载到本地后重复上一步骤

现在,来看源码

已知,vitemonorepo架构的,因此其packages文件夹下的create-vite将会被单独发布,故一定能命中该包,并且,该包的package.jsonbin字段中存在与包name同名的create-vite,其对应的值最终指向的是packages\create-vite\src\index.ts文件

{
  "name": "create-vite",
  ...
  "bin": {
    "create-vite": "index.js",
    "cva": "index.js"
  }
  ...
}

进入packages\create-vite\src\index.ts

首先获取用户传递的参数

const argv = minimist<{
  t?: string;
  template?: string;
}>(process.argv.slice(2), { string: ["_"] });

接着借助prompts通过控制台收集用户反馈

result = await prompts(...)

然后根据用户输入的反馈信息,到本地模板中进行匹配,比如选择了vuetypescript,最终拼接的结果就是:C:...\vite\packages\create-vite\template-vue-ts

const templateDir = path.resolve(
  fileURLToPath(import.meta.url),
  "../..",
  `template-${template}`
);

获取的这个路径是指向create-vite包内模版文件的绝对路径

image.png

只需要使用node的文件读取命令,将匹配的本地模板copy到用户的目录中就可以了

function copy(src: string, dest: string) {
  const stat = fs.statSync(src);
  if (stat.isDirectory()) {
    /**
      如果是目录,则进行递归
      即遍历目录,重复调用write函数
    */
    copyDir(src, dest);
  } else {
    fs.copyFileSync(src, dest);
  }
}
const write = (file: string, ...) => {
  const targetPath = path.join(root, renameFiles[file] ?? file);
  ...
  copy(path.join(templateDir, file), targetPath);
};
// 读取目录
const files = fs.readdirSync(templateDir);
for (const file of files.filter((f) => f !== "package.json")) {
  // 将目标路径下的资源写入到用户目录
  write(file);
}

代码实现

首先,我们在packages文件夹下新建create-vite目录,并进行初始化。如下,只保留三个参数,这里就不再通过构建工具进行打包了

{
  "name": "create-vite",
  "version": "0.0.0",
  "bin": "index.js"
}

接着,在packages\create-vite下新建index.js文件,它应当是一个立即执行函数

(function () {
  // TODO
})();

现在,来考虑参数部分,我并不打算提供太多客制化的内容,只需要把仓库名称作为一个变量交给用户定义即可,这里使用--projectName来承载

(function () {
  const args = process.argv.slice(2);
  if (args.length && args.length === 2 && args[0] === "--projectName") {
    return;
  }
  console.error("projectName缺失,请使用--projectName yourname");
})();

对于用户传递的项目名称,得将其拼接成绝对路径

const projectName = require("path").join(process.cwd(), args[1]);

create-vite中是将模板事先定义到本地并通过node的文件操作读写到用户本地的,我们这里换种方式,直接使用git将模板clone到用户本地

为此,需要用到子进程的exec函数执行git clone,这里把笔者之前已经配置好的vue3工程宕下来

check();
const exec = require("child_process").exec;
// 这里也可以通过控制台交互的形式将要下载的地址外部化
exec(
  "git clone https://github.com/supanpanCn/vue-blob.git",
  { clone: true },
  (err) => {
    if (err) {
      console.error("模板下载失败,请稍后重试", err);
    } else {
      adjustTemplate();
      console.log("模板下载成功");
    }
  }
);

不过目前clone下来的文件夹名称是仓库名,还需要按照收集到的信息对其进行重命名,不过在此之前,得先判断下目标仓库是否已经存在,如果存在,就对其重新进行下命名并做相应的提示

function check(projectName) {
  const fs = require("fs");
  const path = require("path");
  const prefix = process.cwd();
  const targetPath = path.resolve(prefix, projectName);
  if (fs.existsSync(targetPath)) {
    console.error("仓库已存在,重新命名为:projectName_1");
    adjustTemplate(projectName, "projectName_1");
  }
}

最后,在clone执行成功后,调用adjustTemplate对文件名称做修改就可以了

function adjustTemplate(oldName, newName) {
  const fs = require("fs");
  const path = require("path");
  const prefix = process.cwd();
  const oldPath = path.resolve(prefix, oldName);
  const newPath = path.resolve(prefix, newName);
  if (fs.existsSync(oldPath)) {
    fs.renameSync(oldPath, newPath);
  }
}

调试

packages/create-vite/package.json下新建scripts

"scripts": {
    "dev": "node ./index.js --projectName vite-project"
}

运行dev,模板被从远程下载,并且,其名称也从默认的vue-blob修改成了指定的vite-project

image.png

总结

create-vite其实就是单纯的在玩node的文件操作命令,将文件从位置A移动到位置B

由于其并不清楚用户想要创建的到底是哪一个模板,因此需要借助prompts来创建表单收集用户信息以使其更准确、更智能的工作

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容