如何使用esbuild搭建开发库

2,601 阅读2分钟

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

关于esbuild

image.png 这是截自官网的一张图,可以看到esbuild相比其他构建工具速度有着夸张的差异,目前vue3 + vite搭建的项目在开发模式下也是使用到了esbuild。接下来就介绍下如何使用esbuild搭建一个基于typescript的开发库。

开始

新建一个叫做mylib的文件夹

mkdir mylib && cd mylib && npm init --yes

安装下依赖:

npm i esbuild typescript rimraf --save-dev

在根目录下,增加tsconfig.json文件,可以根据自己的需求添加内容,以下为示例内容:

{
  "compilerOptions": {
    "declaration": true, // 生成声明文件,开启后会自动生成声明文件
    "target": "esnext", // 最新的ES标准
    "lib": ["esnext", "dom"], // TS需要引用的库,即声明文件,es5 默认引用dom、es5、scripthost,如需要使用es的高级版本特性,通常都需要配置,如es8的数组新特性需要引入"ES2019.Array"
    "strict": true, // 开启所有严格的类型检查
    "noImplicitAny": false, // 不允许隐式的any类型
    "esModuleInterop": true, // 允许export=导出,由import from 导入
    "moduleResolution": "node", // 模块解析策略,ts默认用node的解析策略,即相对的方式导入
    "outDir": "lib" // 指定输出目录
  }
}

这里要注意,esbuild并不会生成声明文件,方法是通过tsc命令生成。修改package.json文件,添加module字段,并添加脚本

{
  "name": "mylib",
  "version": "1.0.0",
  "description": "",
  "main": "lib/index.js",
  "types": "lib/index.d.ts",
  "module": "lib/index.js",
  "scripts": {
    "ts-types": " tsc --emitDeclarationOnly --outDir lib",
    "build": "rimraf lib && node ./esbuild.js && npm run ts-types"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "esbuild": "^0.12.1",
    "typescript": "^4.2.4"
  }
}

新建src目录,里面添加index.ts文件,示例内容如:

export const add = (a: number, b: number): number => a + b;

在根目录添加esbuild.js文件,编写esbuild相关代码(详细配置文档请查阅官网)。

const esbuild = require("esbuild");
​
esbuild
  .build({
    entryPoints: ["src/index.ts"],
    outdir: "lib",
    bundle: true,
    sourcemap: true,
    minify: true, // 压缩代码
    splitting: true,
    format: "esm",
    target: ["esnext"],
  })
  .catch(() => process.exit(1));

运行npm run build,可以看到成功生成lib目录,并且里面包含了声明文件

image-20210824161345547.png

额外内容

将script内容写入到html文件:在src目录下先新建一个html文件

const esbuild = require("esbuild");
const { readFile, writeFile, mkdir } = require("fs").promises;
​
(async () => {
  await mkdir("./lib");
​
  const script = esbuild.buildSync({
    entryPoints: ["src/index.ts"],
    bundle: true,
    minify: true,
    format: "esm",
    target: ["esnext"],
    write: false,
  });
​
  const html = await readFile("src/index.html", "utf8");
​
  const content = html.replace(
    "<body>",
    `<body><script type="module">${script.outputFiles[0].text}</script>`
  );// 将script插入到body标签内
  await writeFile("lib/index.html", content);
})();
​

对html内容进行压缩处理,安装压缩包:

npm i html-minifier-terser --save-dev
const esbuild = require("esbuild");
const { readFile, writeFile, mkdir } = require("fs").promises;
const minify = require("html-minifier-terser").minify;
​
(async () => {
  await mkdir("./lib");
​
  const script = esbuild.buildSync({
    entryPoints: ["src/index.ts"],
    bundle: true,
    minify: true,
    format: "esm",
    target: ["esnext"],
    write: false,
  });
​
  const html = await readFile("src/index.html", "utf8");
​
  const content = html.replace(
    "<body>",
    `<body><script type="module">${script.outputFiles[0].text}</script>`
  ); // 将script插入到body标签内
​
  const minifyOptions = {
    collapseWhitespace: true,
    keepClosingSlash: true,
    removeComments: true,
    removeRedundantAttributes: true,
    removeScriptTypeAttributes: true,
    removeStyleLinkTypeAttributes: true,
    useShortDoctype: true,
    minifyCSS: true,
  }; //压缩配置
​
  const fileContent = minify(content, minifyOptions);
​
  await writeFile("lib/index.html", fileContent);
})();