带德诺去兜风

2020-05-30 15:26:46

当我第一次听说Deno 1的时候,我以为它会失败。更好的Node听起来不错,但我看不出它为什么会流行起来。当它达到1.0并在黑客新闻上爆炸时,我既惊讶又有点兴奋。也许它的蒸汽比我想象的要多。节点有疣,打字稿毕竟很棒。读了这篇声明的博客文章,有两点让我印象深刻:

尽管熟悉Node生态系统,但我仍然对必须阐明我对标准库提供的所有绝对基本需求的依赖关系感到恼火。这对于大型、成熟的项目来说并不是很重要。但是,当您编写大量一次性的、无关的脚本时,痛苦就会累积起来。我发现使用流特别困难,并且惊讶于有这么多不兼容的方式来做同样的事情。当溪流起作用时,它们是很棒的。当他们不这样做的时候-这是非常令人困惑的。

因此,对于日常脚本,也许ts-node或go会是更好的选择。但是我有一个一次性脚本可以解决的小问题-所以我想我会带Deno试驾一下。

在介绍我的简单问题之前,我设置了一个hello world脚本。我使用安装页面中的安装说明:

BREW安装Deno mkdir rename-files&;&;rename-files&39;console.log(";hello,World!";)';>;hello-world.tsdeno运行index.tsCompile file:/hello-world.tshello,World。

环顾四周,我原本希望从库中提取的许多方法都暴露在Deno全局(例如:deno.cwd()-这些方法列在runtime API下面。标准库是独立的,并且有有趣的文档。它似乎大部分是注释源代码,偶尔的readme.md呈现为HTML。我喜欢这种简约,但不会逗留太久。安装页面提供了用法示例:

从";https://deno.land/[email protected]/http/server.ts";导入{SERVE};const s=SERVE({port:8000});控制台。日志(";http://localhost:8000/";);等待(%s的常量请求){请求。Response({body:";Hello World\n";});}。

如果您熟悉Node,那么import语句可能最为突出。这是来自url的导入行为,被吹捧为npm和Package.json的替代方案。指定标准库的版本对我来说似乎很奇怪--我本以为它会自动(神奇地)绑定到我使用脚本运行的Deno的任何版本(并随脚本一起下载)。感觉很奇怪,但我可能没有足够的上下文来理解。稍后会详细介绍这一点。

经过反复试验,我让脚本使用Deno run--allow-net example.ts运行。与Node不同,deno要求您授予各种io工具的权限;可以使用deno run-h列出它们。这是直截了当的解决办法。

基本设置就绪后,我想我已经准备好解决手头的问题了。

我写日记已经有几年了,4-5年前我决定用一种特定的格式来存储文件:

在此之前,我有几个使用各种其他约定的日记帐,但它们始终是分组到命名文件夹中的标记文件:

我想把它们批量转换成其他格式。我可以通过以下方式做到这一点:

读取文件目录,向下过滤到标记-我们可以使用Node中的readDir来实现这一点,但我通常使用Klaw。当我涉足围棋时,我想起这是它的标准库(Walk)的一部分,所以我去Deno‘s搜索同等的东西,我很惊讶地发现了它。页面本身被注释为源代码…。某些页面呈现标记(如果有Readme.md)。我不会纠结于这种风格的持久性,但我欣赏今天的简约。果然,WALK在Deno标准库中。在标准库页面中:

这些模块根据DENO版本进行标记。因此,例如,保证v0.3.0标记可以与Deno v0.3.0一起使用。您可以使用网址https://deno.land/[email protected]/.链接到v0.3.0。不指定标记将链接到主分支。

我按照指令导入Walk,然后使用异步迭代器syntnax编写一个简单的循环:

从";https://deno.land/[email protected]/fs/mod.ts";;异步函数Readin(srcDir:String){Const Proceded=New<;String,String&>;();导入{WALK}等待(WALK(srcDir,{IncluddeDir:False,EXTS:[";MD";,";mdown";],})){控制台。日志(条目);}}。

编译file:/Users/cloverich/code/scripts/convert-ls-chron-deno/index.tsDownload https://deno.land/[email protected]/fs/mod.tserror:未捕获错误:导入';https://deno.land/[email protected]/fs/mod.ts';失败:在异步进程导入($Deno$/编译器.ts:736:23)的Object.sendAsync($Deno$/ops/Dispatch_json.ts:98:10)的UnwrapResponse($Deno$/ops/Dispatch_json.ts:43:11)中找不到404异步进程导入($Deno$/编译器.ts:753:7)的异步进程导入($Deno$/Compiler.ts:753:7)中的UnwrapResponse($Deno$/ops/Dispatch_json.ts:43:11)中的unwrapResponse($Deno$/ops/Dispatch_json.ts:98:10)。

就这么多了。我一定是把进口写错了。经过一番挖掘之后,我很不情愿地删除了该版本并链接了主分支。但这会导致另一个错误,并且非常令人困惑:

编译file:/Users/cloverich/code/scripts/hello-world.tserror:TS2339[错误]:属性';uTime';不存在于';Typeof Deno';等待Deno.utime(DEST,statInfo.atime,statInfo.mtime);~位于https://deno.land/std/fs/copy.ts:90:16#.。更多的错误。

以下所有模块都在mod.ts中公开此功能当前不稳定。要启用它,请使用Deno Run-Ustant。

eno run--不稳定的hello-world.ts编译文件:///Users/cloverich/code/scripts/convert-ls-chron-deno/hello-world.ts{路径:";/Users/cloverich/wiki/notes/2013-07-11.md";,名称:";2013-07-11.md";,isFile:True,isDirectory:False,isSymlink:False}#.。等。

在Node中,我可以使用fs.readFile读取文件。我通常会把这个用util.promisify包起来,这样会更好吃。德诺有点不一样:

有趣的是,这不需要进口。Deno在其全局名称空间中公开了一些WebAPI(我需要更多地了解这一点);TextEncoding似乎是编码API的一部分。无论如何,在Node中,编码可以在对readFile的调用中指定。经过一些搜索之后,Deno在STD(Read_File_Str)中提供了一个方便的帮助器。

继续编写,我天真地在if/Else中写出文件,假设我会在Deno中找到与appendFile fs等价的文件。

Deno通过write_file_str提供了进一步的增强,但是它不接受用于切换追加文件模式的选项对象。哎呀。

嗯,我可以解释我所有的想法,但这并不有趣。下面是最后的简短脚本:

从";https://deno.land/std/fs/mod.ts";;const{读文件,写文件}=取消;//查看读文件/写文件调用常量解码器=NEW(";UTF-8";);常量编码器=NEW();导入{WALK,ENURREDIR}。/*使用My Folder/FileName约定将目录内容合并到另一个目录中**@param dateToContents-截断日期字符串(YYYY-MM-DD)到文件内容的字典*@param dir-输出目录*/async函数mergeInto(dateToContents:map<;string,string>;,dir:string){for(const[dateStr,Contents]of dateToContents){for(const[dateStr,Contents]of dateToContents)。Entries()){//生成文件夹+文件名:yyyy/MM/YYYY-MM-DD.md//ex:2020/03/2020-03-15.md const ymdir=`${dir}/${dateStr。Slice(0,4)}/${dateStr.。Slice(5,7)}`;const outfile=ymdir+";/";+dateStr+";.md";;等待保证目录(Ymdir);等待WriteFile(输出文件,编码器。Encode(Contents),{append:true});}}/*遍历源目录,收集所有文件内容。*@param srcDir*/异步函数readin(srcDir:string){const Processing=new<;string,string>;();for AWait(等待)(WAIT(srcDir,{include deDir:false,exts:[";md";,";mdown";],}){const fileInfo=await stat(entry.path);//i。我将根据上次修改时间Const dateStr=fileInfo.mtime!将文件重命名为yyyy-mm-dd。toISOString()。切片(0,10);常量内容=解码器。DECODE(等待readFile(entry.path));IF(已处理。Has(DateStr)){//追加常量现有=已处理。Get(DateStr);已处理。set(dateStr,Existing+";\n";+Contents);}否则{已处理。set(dateStr,Contents);}}返回已处理;}const wikiLogs=等待读取(";/Users/cloverich/wiki";);等待mergeInto(wikiLogs,";/Users/cloverich/Notes/Some-Journal";);

该程序简单易懂,在我阅读时只需添加即可进一步简化;当我意识到这一脚本即将完成时,我才意识到这一点。所以我会把这项工作留到下次拜访时再做。

我喜欢用德诺写这个小剧本。除了关于导入和使用标准库的一些混乱之外,它相对来说是一帆风顺的。不必为琐碎的任务(如mkdirp和walk)引入第三方依赖项是一件令人耳目一新的事情,我可以想象自己会使用Deno来完成更多这样的一次性脚本。

虽然德诺现在人气飙升,但这是昙花一现吗?如果运行库和标准库继续成熟,我认为对于熟悉javascript但想要更少样板的人来说,deno可能是比ts-node更实用的选择。这会演变成一个成熟的生态系统吗?我不确定,但看起来不像一年前那么牵强了。我认为熟悉Node编程的人在Deno领域不会是一条离水的鱼,所以我认为没有必要“跟上”它的变化;我认为您应该使用感觉更实用或更令人愉快的任何一种。

一个更难回答的问题(至少对我来说)是,为什么选择Deno而不是Go?我在这里更矛盾。如果Deno的优势是从Go借来的标准库-为什么要麻烦呢?我已经试过几次了,发现它大多是直觉的。我记得字符串解析和JSON操作稍微复杂一些,甚至令人沮丧。当您习惯于将JSON作为一级动态对象操作时,很难想象还有什么比这更实用的了。尤其是对于一次性脚本编写需求。

Ryan Dahl在2018年6月发表了关于Node.js的10件我后悔的事情,他在会上反思了自己在Node早期开发中所犯的错误。最后,他介绍了迪诺,这是他正在做的一个玩具项目。