2023.06.03前端周刊

252 阅读2分钟

新闻

Bun发布新功能Bun Macros

通过macros,可以仅将一段代码运行后的结果打包进最终代码中,而非将整段代码都打包进最终代码

举个例子,下面这段代码

// random.ts
export function random() {
  return Math.random();
}
// cli.tsx
import { random } from './random.ts' with { type: 'macro' };

console.log(`Your random number is ${random()}`);

通过bun build打包后得到的脚本如下

console.log(`Your random number is ${0.6805550949689833}`);

可以看到,random函数并未出现在打包后的脚本中,Bun在打包过程中直接执行了cli.tsx中的random(),并用其执行结果替换了random(),生成了最终的脚本

Dead code elimination

Bun在执行macros并用执行结果替换了目标代码后,会删除dead code,比如下面这段代码

// returnFalse.ts
export function returnFalse() {
  return false;
}
import {returnFalse} from './returnFalse.ts' with { type: 'macro' };

if (returnFalse()) {
  console.log("This code is eliminated");
}

由于执行returnFalse()后得到的结果是false,条件块内的语句永远不会执行到,故打包后得到的将是一个空文件,里面没有任何代码

Security considerations

  1. macro只要不被调用(即使被import了),就对打包后的代码没有任何影响和副作用,因为它根本不会被打包进代码中。

  2. 可以通过--no-macros来在打包的过程中禁用macro,此时如果代码中如果使用macro,将会报错如下

  3. 为了减少恶意package对用户的潜在攻击,macro在node_modules禁用。如果一个package试图调用macro,将会报错如下:

    不过用户可以将package作为macro引入并调用

    import {macro} from "some-package" with { type: "macro" };
    
    macro();
    

Limitations

macro的执行结果必须是可序列化的

序列化即把对象转化为可传输的字节序列过程

下面这段代码,由于macro的执行结果是函数(不可序列化),因此是不合法的

export function getText(url: string) {
  // this doesn't work!
  return () => {};
}

总结下,macro的执行结果可以是以下类型:

  1. JSON-compatible的数据类型,比如下面这段代码

    export function getObject() {
      return {
        foo: "bar",
        baz: 123,
        array: [ 1, 2, { nested: "value" }],
      };
    }
    
  2. macro可以是异步函数,其执行结果也可以是Promise实例

    export async function getText() {
      return "async value";
    }
    
  3. Bun的转码器实现了对一些常见的数据格式(比如ResponseBlogTypedArray)进行序列化的功能,比如下面这段代码,由于fetch的执行结果是Promise<Response>,该函数作为macro是合法的

    export function getObject() {
      return fetch("https://bun.sh")
    }
    

macro的参数必须是静态的

比如下面这段函数作为macro就是不合法的

import {getText} from './getText.ts' with { type: 'macro' };

export function howLong() {
  // the value of `foo` cannot be statically known
  const foo = Math.random() ? "foo" : "bar";

  const text = getText(`https://example.com/${foo}`);
  console.log("The page is ", text.length, " characters long");
}

工具

Aimless.js

一个用于生成各种随机值的库

image.png