npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

fresh-shell

v3.2.0

Published

The fully customizable JavaScript shell, for everyday use.

Downloads

34

Readme

fresh-shell

基于 Node.js 的 JavaScript「壳中壳」

image

介绍

Shell 是 Unix 世界中一个不可或缺的工具,但它的语法也许不那么优雅、难以学习,阻止了你很多突发奇想,让很多需要高生产力的临时需求不得不变成写一个冗长的 Node 脚本再去调试运行。

例如,你如何批量重命名一大堆文件?如何把一个文件夹里所有的 png 文件都调用一次 tinypng CLI,并用得到的压缩图片文件替换原文件?对于没有系统学习过 Shell 语法的开发者,这些任务都是艰巨的,写一个这样的脚本往往要用到 Node.js,同时一些细小的任务还是需要用到 Shell。

人们曾经想过改进 Shell 的语法,但事实证明,这是一个大工程,而且是一个社会工程。我们常用的 Zsh 对 Bash 兼容性很好,但它语法也就跟 Bash 一样复杂;假如我们下决心用上稍微优雅一些的 Fish,如何把别人留在开发文档里的 Bash 复杂命令改成 Fish?如何去说服你的同事跟你一起用 Fish 来避免分享高级用法的过程中出现语法冲突?

有开发者想到了将两种语法融合,Xonsh 就是这样一个产物。但经过长期使用,我们会发现 Xonsh 并没有实现全部的 Shell 语法,很多语法都是缺失的,这些问题的根源在于 Xonsh 试图把两个语法糅合在一起,而不是区分它们的上下文。

Fresh 是一个构造极简的 JavaScript Unix Shell。它能在不破坏语法的前提下,让 JavaScript 语法和任意一种标准 Shell 的语法融合,并同时满足你基于 Shell 语法的日常使用和基于 JavaScript 的 Hacking。

为什么不破坏语法?因为 Fresh 区分上下文,使用自动补全的 Tag Function 来处理 Shell 语法。

安装和使用

安装 Fresh 之前,请确保你的 Node.JS 已安装并位于 /usr/local/bin/node 下。

rikumi $ npm i -g fresh-shell
rikumi $ fresh

rikumi ƒ`|

安装后,你可以将 fresh 的入口程序(通常为 /usr/local/bin/fresh)设置为终端和 VS Code 的默认 Shell。

Fresh 中必备的两个字符是 ƒ`,我们可以称之为「软提示符」(位于标准输入流而非输出流中、可以删除和修改的提示符)。软提示符的存在让我们可以直接键入 Shell 命令,而不需要多余的语法。

输入任何你需要的 Shell 命令(如 ls),回车之后,Fresh 会自动补全末尾的反引号 `,因此会产生 Tag Function Call 语法:ƒ`ls`,其中函数 ƒ 的含义为同步执行 Shell,因此 ls 命令会被执行。这就是 Fresh 的基本工作原理。

如何输入 ƒ`

使用 option+F 可以输入 ƒ;`(反引号)是半角状态下 Tab 上方的按键。

交互执行和隐藏执行

ƒ 函数(exec 函数)有两种模式:交互执行和隐藏执行。

当输入的命令行是单一的 ƒ 调用时,会进入交互执行状态,输入输出以标准输入输出的形式提供;当 ƒ 调用是输入的表达式中的一部分,会进入隐藏执行状态,标准输出和标准错误输出不会直接上屏,而是作为 ƒ 函数的返回值提供。

ƒ 函数的返回值是一个特殊字符串,字符串的值为子程序的标准输出(经过 UTF-8 解码得到的内容);该字符串上附加有 statusstderrerror 三个属性,分别表示状态码、标准错误输出(经过 UTF-8 解码得到的内容)和 JavaScript 错误对象。

下图中分别展示了命令 git status 的交互执行模式,以及隐藏执行模式下的 statusstderrerror 属性。

image

内置指令

Fresh 本身并不是 Unix Shell,而是 Unix Shell 的一层封装,所有的 Shell 命令都是在子进程中执行的。这也就意味着 Fresh 执行的 Shell 无法改变 Fresh 本身的状态。而一部分状态是需要跨命令保持的,其中就包括工作目录(CWD)环境变量以及别名

如果我们用 Shell 模式执行 cd/export/alias 语句,它们退出后不会对 Fresh 本身产生任何影响;因此,Fresh 实现了 cd/export/alias 内置指令,让单一的 cd/export/alias 语句能够工作。

注意,Fresh 只支持在单一语句中独立使用 cd <directory>export <KEY>=<value>alias [<command>=<replacement>] 的语法,并支持在其中进行简单的环境变量插值;复杂的用法将会以 Unix Shell 模式执行,导致它们不会对 Fresh 本身的状态产生影响。以下是一个对比简单 cd/export 语句和复杂 cd/export 语句的例子。

image

可以看到,简单的 cd/export/alias 语句会被 Fresh 捕获,产生的效果会对后面的指令生效;复杂的 cd/export/alias 语句会采用 Shell 模式进行执行,因此不会对 Fresh 本身以及后面的指令产生影响。

Node.js 环境与自动 require

Fresh 的本质是一个 Node.js REPL(但并没有使用 Node.js REPL 库,而是用表现更稳定、可定制性更强的 Node.js Readline 库进行实现),其中只有 ƒ 函数是与 Shell 执行相关的;除 ƒ 函数之外,Fresh 也是一个完整的 Node.js 执行环境。

相比标准 Node REPL,Fresh 面向终端场景,加入了自动 require 的能力,即在全局命名空间下,找不到的对象,会自动尝试 require。同时,在配置文件中也可以增加新的自动导入函数。

image

融合使用

借助上述特性,可以将 Shell 命令与 JavaScript 融合使用,Shell 中使用模板插值语法 ${} 可以嵌入 JavaScript 表达式;JavaScript 中也可以使用 ƒ 函数嵌入 Shell 执行结果:

image

配置文件

Fresh 支持使用配置文件进行自定义,实现嵌套 Shell 和配置继承、提示符美化(如实现简易的 Powerline 风格)、定制 JavaScript 环境、定制 Tab 自动完成、定制颜色高亮等。

配置文件位于 ~/.freshrc.js,会在 Fresh 启动时被执行。配置文件中可以对全局配置对象 config 进行修改。预设的 config 对象参见这里

定制动词

修改 config.verb,可以将默认的动词 ƒ 修改成其他合法的 JavaScript 标识符

image

嵌套 Shell 与执行前指令

Fresh 可以使用 Bash、Zsh、Fish、Xonsh 等任何支持 -c 参数的第三方 Shell 作为内嵌 Shell 工具,只需在配置文件中更改 config.shell 即可:

config.shell = '/bin/zsh';

被嵌套的 Shell 默认以 --login(Login + Interactive)方式执行,在该模式下,Shell 将会加载更多的默认环境变量,但不会自动加载配置文件。

如要修改被嵌套的 Shell 的附加执行参数(默认为 ['--login']),可以在配置文件中操作 config.shellArgs。注意,-c 将会自动添加;

如需加载 .bashrc/.zshrc 等配置文件(不推荐),可以在 config.shellCommandPrefix 中设置需要添加到每次 Shell 被执行字符串之前的指令,例如 config.shellCommandPrefix = 'source ~/.bashrc;',注意不要忘了以分号结尾。

配置文件中执行 Shell 语句

在配置文件中,可以像在 Fresh 内一样使用 ƒ 函数,但固定处于隐藏执行模式,如果要查看输出,需要配合 console.log 等方式。

请注意对 config.shell 的修改与执行 Shell 语句之间的先后顺序。

定制提示符(以 Powerline 风格为例)

为了方便自定义,配置对象中 config.prompt 函数被拆成三个函数:config.promptconfig.gitconfig.cwd(实际被 Fresh 调用的只有 config.prompt 函数),以便于分别改写提示符样式、Git 显示格式和 CWD 显示格式。

这里以 Powerline 风格提示符为例,展示如何改写提示符样式。

const chalk = require('chalk').default;

config.prompt = (status = 0) => {
    const bgColor = status ? chalk.bgRed : chalk.bgBlue;
    const fgColor = status ? chalk.red : chalk.blue;
    const bgBlack = chalk.bgBlack;
    const fgBlack = chalk.black;
    const fgWhite = chalk.white;
    const git = config.git();
    if (git) {
        return bgColor(' ' + fgBlack(config.cwd()) + ' ') +
            bgBlack(fgColor('') + fgWhite(config.git()) + ' ') +
            fgBlack('') + ' ';
    } else {
        return bgColor(' ' + fgBlack(config.cwd()) + ' ') +
            fgColor('') + ' ';
    }
}

效果如下:

image

定制 Tab 自动完成

Fresh 默认配置中提供了路径自动完成和 Git 分支自动完成;你还可以修改 config.complete 函数来完善这一特性。自动完成函数的写法参见 Readline 文档

定制 history 长度

键入过的最近 100 条语句会保存在 ~/.fresh_history 文件中,可以修改 config.historySize 来改变这个数字。

定制语法高亮

为了方便自定义,默认配置对象中将语法高亮分为四个函数:config.colorizeTokenconfig.colorizeCodeconfig.colorizeCommandconfig.colorizeOutput,它们分别对应于对单个 JavaScript Token 的高亮、对一段代码的 Token 解析与高亮、对输入命令的高亮以及对 JavaScript 输出结果的高亮。实际被 Fresh 调用的只有 config.colorizeCommandconfig.colorizeOutput 两个函数。

定制进程标题

改变 config.makeTitle 函数可以定制 Fresh 的进程标题,用于显示在 GUI 终端中。该函数接受零个或一个参数,接受零个参数时,需要返回 Fresh 处于空闲状态时的进程标题;接受一个参数时,该参数是要执行的命令行程序的 argv 列表,需要返回 Fresh 执行该子程序时的默认进程标题。

建议与贡献

欢迎对本项目提出 Issue 或 Pull Requests。需要注意的是,Fresh 作为一个外壳程序,对于功能上的要求会进行一定的取舍,在保持实现简单的前提下合理迭代。

因为 Fresh 的最大意义是用 < 500 行代码实现 JavaScript 与其他 Shell 的融合,而非做一个完美无缺的 Shell 本身。