deno怎么复用node的包

上文说过,deno目前最大的问题是生态,也就是说仍无法无缝继承nodejs的生态。那它也意味着可以使用一部分nodejs的资源。

以AST(抽象语法树)为例,它在底层工作时还是很有必要的,我们可能会用它写个小工具,实现一些简单的代码转换。但我们在deno.land中查找第三方库,却没有找到合适的。
deno怎么复用node的包_第1张图片

怎么办?
这时你首先想到的不该是“正是我辈大展身手造轮子的时候到了”,而是应该想,node的npm包是不是可以复用?

node版本

AST有各种实现版本,比较出名的是babel。这里我找两个简单的esprimaescodegen

const esprima = require('esprima');
const escodegen = require('escodegen');

const encodeJs = function () {
    const str = `
    global.x = 10;
    name += '-abc';
    age = 12;
    `;
    const AST = esprima.parse(str);
    // console.log(AST);
    // console.log(AST.body[0].expression.left.object.name);
    // console.log(result.body[0].consequent.body[0].expression.left);

    AST.body.splice(0, 1); // remove global.x = 10;

    const originReback = escodegen.generate(AST);
    console.log(originReback); // name += '-abc';age = 12;
};

encodeJs();

我们先在npm上看下这两个包的依赖情况:
deno怎么复用node的包_第2张图片
deno怎么复用node的包_第3张图片
一个零依赖,一个依赖了5个包。还是很有希望通过CDN转义过来的。

deno版本

我们试试CDN(通常有https://esm.sh/https://cdn.skypack.dev/两个,这里任选一个)转下:
https://cdn.skypack.dev/esprima
deno怎么复用node的包_第4张图片
https://cdn.skypack.dev/escodegen
deno怎么复用node的包_第5张图片
似乎是可以的,于是我们可以试下:

import * as esprima from 'https://cdn.skypack.dev/esprima';
import * as escodegen from "https://cdn.skypack.dev/escodegen";

const encodeJs = function () {
    const str = `
    global.x = 10;
    name += '-abc';
    age = 12;
    `;
    const AST = esprima.parse(str);
 
    AST.body.splice(0, 1); // remove global.x = 10;

    const originReback = escodegen.generate(AST);
    console.log(originReback); // name += '-abc';age = 12;
};

encodeJs();

执行deno run xx.js,成功!

真是个愉快的消息。这意味着我们不需要再造轮子。这样即使这两个包有什么bug,你只需要去github提issue,到时候升级版本就行了。

注意

不过有点儿需要注意,假设你要在项目中使用,最好将版本号写上,比如:

import * as esprima from 'https://cdn.skypack.dev/esprima@v2.0.0';

但这样还不够,如果你要使用deno的lock.json文件来保障项目各成员或各个环境都能使用相同的版本,我们所用的这个CDN并不能保障它的hash值不变化(也就是说隔断时间对应的hash值会变化,也会导致deno cache --lock=lock.json校验失败),即使版本号是固定的。
那么应该怎么办呢?

再回头仔细看上面CDN生成的文件内容:
deno怎么复用node的包_第6张图片
这里有个Pinned URL,它便是正解。
所以,最终的代码变成这样:

import * as esprima from 'https://cdn.skypack.dev/pin/esprima@v4.0.1-2bFId5CSQMZAdjdZliQ2/mode=imports/optimized/esprima.js';
import * as escodegen from "https://cdn.skypack.dev/pin/escodegen@v2.0.0-MB39rvARiyYcecqDvQhe/mode=imports/optimized/escodegen.js";

const encodeJs = function () {
    const str = `
    global.x = 10;
    name += '-abc';
    age = 12;
    `;
    const AST = esprima.parse(str);
  
    AST.body.splice(0, 1); // remove global.x = 10;

    const originReback = escodegen.generate(AST);
    console.log(originReback); // name += '-abc';age = 12;
};

encodeJs();

缺点

我们使用deno,推荐直接使用TS,但这个包没有ts文件,也就没有类型文件。到github上看下:
deno怎么复用node的包_第7张图片
果然是用js写的。
针对这种情况,我们要么这么忍了,要么只能自己给写个.d.ts文件了。

typescript类型推断

对于本身是用ts写的npm包,以jwfetch为例,只需要在后面加个?dts即可,如:

import { BaseAjax } from "https://cdn.skypack.dev/jwfetch?dts";

运行代码,控制台显示:image.png
我们看到,类型文件.d.ts都下载下来了。这样类型推断就有了。

但要按上面说的,固定我们的版本与hash的话,就只能:

import { BaseAjax } from "https://cdn.skypack.dev/pin/jwfetch@v1.1.2-1LpCExuOCuNK2LPTj1mF/mode=imports/optimized/jwfetch.js";

这种情况又会有个问题,这只是一个js文件,并没有类型文件,怎么办?别慌,只需要加// @deno-types就行了:

// @deno-types='https://cdn.skypack.dev/-/jwfetch@v1.1.2-1LpCExuOCuNK2LPTj1mF/dist=es2019,mode=types/dist/index.d.ts'
import { BaseAjax } from "https://cdn.skypack.dev/pin/jwfetch@v1.1.2-1LpCExuOCuNK2LPTj1mF/mode=imports/optimized/jwfetch.js";

BaseAjax.defaults.baseURL = "https://api.github.com";
console.log(BaseAjax);

总结

本文以AST为例,教你在使用deno进行开发的过程中,如何利用CDN来复用npm的node包。node.js已经发展了十余年,其中积累了不可计数的资源,我们要尽可能地复用它们,以提升开发效率。

你可能感兴趣的