手把手教你用Deno写个脚手架

379 阅读3分钟

如果你是个前端工程师,你对脚手架就不会陌生。React、Vue、Angular各大框架都不止一个脚手架,现在之所以几乎成为一个前端框架的标配,那是因为各框架无不是依赖Node.js进行开发的,需要集成webpack、rollup、vite之类工具才能开发或生产。脚手架的出现极大地节约了你的配置成本,那对于业务层面而言是没有意义的浪费。

Node.js能做到的事情,Deno显然也能做到。比如你的团队将使用Deno进行后端开发,那么大概率需要一个自己的脚手架,免于让每个成员复制粘贴模板代码,再一一修改配置的苦恼。

下文将教你怎么开发一个脚手架。

准备

首先,你需要规划一下,我们的脚手架需要哪些步骤。

就像把大象装进冰箱需要3步一样,脚手架也不例外:

  1. 下载模板代码
  2. 解压模板代码
  3. 修改模板代码

所以你的前提是得有个地址存放模板代码,并可以让外界下载到。github提供了这个能力,找到你的工程,在选择下载那里,把链接地址复制出来即可。 image.png 路径大概是这样的: github.com/用户名/工程名/arc…

也可以发布一个tag,到这里找地址: image.png

地址通常是:github.com/用户名/工程名/arc…

下载

Deno下载远程文件很简单,直接用fetch API请求后再写入本地文件就可以了:

export async function download(url: string, fileName: string) {
  const response = await fetch(url, {
    method: "GET",
    headers: {
      "Content-Type": "application/zip",
    },
  });
  const buffer = await response.arrayBuffer();
  return Deno.writeFile(fileName, new Uint8Array(buffer));
}

解压

解压我们在deno.land上面找一个星多点的包:deno.land/x/zip@v1.2.…。 用法很简单:

import { decompress } from "https://deno.land/x/zip@v1.2.3/mod.ts";

console.log(await decompress("myfile.zip")); //=> ./

解压后,不要忘了删除这个zip包:

await Deno.remove(zipName);

修改

模板代码当然是有些地方要修改的,最常见的是修改工程名,也就是把模板工程的名称修改为自定义的名称。 这个自定义名称从哪儿来呢?一般是从命令行参数中获取,比如Deno.args[0]。比如我们最终的cli命令是deno_cli,调用时是deno_cli aa,那么Deno.args[0]就是aa。

如果参数比较多,也可以使用deno.land/x/ask,它是问答式的交互页面: image.png

修改工程名是非常简单的,Deno的rename API就可以:

await Deno.rename(原名称, 新名称);

如果你的模板代码的README.md文件里也带了模板名称,那么你可能还要把它替换:

import { join } from "https://deno.land/std@0.120.0/path/mod.ts";

async function writeReadme(name: string) {
  const realPath = join(name, "README.md");
  const doc = await Deno.readTextFile(realPath).catch((_) => null);
  if (!doc) {
    console.warn(`没有找到【${realPath}】`);
    return;
  }
  const newDoc = doc.replaceAll(templateName, name);
  await Deno.writeTextFile(realPath, newDoc);
}

再有其它文件的替换可自行实现。

最终这个文件的核心代码为:

async function main() {
  let name = Deno.args[0];
  if (!name) {
    const ask = new Ask({
      prefix: ">",
    });
    // deno-lint-ignore no-explicit-any
    const answers: any = await ask.prompt([
      {
        name: "name",
        type: "input",
        message: "projectName:",
      },
    ]);
    name = (answers as Params).name;
  }

  await download(url, zipName);
  await decompress(zipName, "./");
  await Deno.rename(projectName + "-" + branchName, name);
  await Deno.remove(zipName);

  await writeReadme(name);

  console.log(`init project ${projectName} end`);
}

完整样例可参见这里

验证

假设你的文件名称为project.ts,那么可以本地运行验证:

# 可以把参数加上
deno run --allow-write --allow-read --allow-net --allow-run --unstable ./project.ts

看本地目录是不是下载了一个文件夹,名称和内容是不是符合你的预期? 如果没有问题,再安装后验证:

# 全局安装
deno install --allow-write --allow-read --allow-net --allow-run --unstable -n deno_cli -f ./project.ts

# 使用
deno_cli
# 或者
deno_cli my_app

如果都没有问题,就可以把你的脚手架发布到deno.land上,以后在任何一台安装了Deno的机器上都可以一行命令安装使用你的脚手架了。

deno install --allow-write --allow-read --allow-net --allow-run --unstable -n deno_cli -f https://deno.land/x/你的命名/project.ts

怎么发布请见上篇手把手教你发布一个Deno模块