论 Node 如何快速实现 Alfred 工作流?

1,402 阅读8分钟

Kapture 2023-02-06 at 17.20.03.gif

前言

前段时间趁着空闲时间整理了下之前基于 Node 写的一个 Alfred 工作流, 工作流的主要功能如上图所示,通过关键词、以及搜索词条快速在浏览器上打开 Antd 组件文档; 本文将手把手演示整个工作流的搭建流程, 当然, 这里实现的功能略有删减, 完整代码可参考 GitHub;同时欢迎大家下载使用: alfred-antd

GitHub 参考地址: github.com/KunLunXu0-0…

NPM 包地址: www.npmjs.com/package/alf…

一、初始化项目

复制下列命令, 再终端中执行, 其实注释不删除会有报错, 不过不影响命令执行结果

mkdir alfred-share && \ # 创建项目目录
cd alfred-share && \ # 进入项目目录
npm init -y && \ # 初始化 npm 项目
git init  && \ # 初始化 git
echo node_modules > .gitignore # 创建 .gitignore 并添加内容: node_modules

二、添加 Alfred 工作流基础配置

这一步的主要目的是, 为项目添加 Alfred 工作流的配置模板, 这里需要先在 Alfred中创建一个空的工作流, 再复制使用

2.1 获取 info.plist 配置模板

  1. 创建 Alfred 临时工作流: 偏好设置(Preferences) -> 工作流(Workflows) -> 点击 + -> Blank Workflow -> 填写信息 -> create

Kapture 2022-07-05 at 11.48.09.gif

  1. 复制 info.plistnpm 项目下: 选中临时工作流 -> 右键 -> Open in Finder -> 将目录中的 info.plist 复制到 npm 项目根目录下即可

Kapture 2023-02-03 at 13.26.51.gif

  1. 删除临时工作流: 选中工作流 -> 右键 -> 删除(Delete)

三、项目基础搭建

3.1 安装依赖 alfy

alfy是基于 node 实现 一个 Alfred 工具库, 能够帮助我们轻松创建 Alfred 工作流

npm i alfy

3.2 修改项目配置文件 package.json

  1. 添加 "type": "module" 配置: 使用 ES 模块处理 js文件
  2. alfy-init 命令是 alfy 带有的一个命令, 用于 初始化(安装) 工作流
  3. alfy-cleanup 命令是 alfy 带有的一个命令, 用于 清除(卸载) 工作流
  4. postinstallnpm 中的一个钩子, 在当前项目中执行 npm install 结束后, 会自动执行钩子设置的命令(初始化工作流)
{
  "name": "alfred-share",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
+ "type": "module",
  "scripts": {
+   "start": "alfy-init",
+   "clear": "alfy-cleanup",
+   "postinstall": "npm run start"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

3.3 安装、卸载工作流

从上文可知, 要 安装 工作流, 实际上我们只要执行 alfy-init 即可, 我们可以通过执行下面 任意一条 命令来进行安装工作流

# 方式一: 通过 npm install 命令, 当执行完后会触发 postinstall 钩子, 执行 npm run start
npm install 

# 方式二: 执行我们设置的 npm 脚本, 实际上执行的是 alfy-init
npm start 

# 方式三: 直接执行 alfy-init
npx alfy-init 

卸载工作流, 有两种方式

  1. 直接在 Alfred 中将工作流删除即可
  2. 通过执行 alfy-cleanup 进行卸载, 你可以通过执行下面 任意一条 命令来进行卸载
# 方式一: 执行我们设置的 npm 脚本, 实际上执行的是 alfy-cleanup
npm run clear 

# 方式二: 直接执行 alfy-cleanup
npx alfy-cleanup 

四、开胃菜

实现 Alfred 工作流: 快速打开掘金首页、活动中心、资讯中心

Kapture 2022-07-06 at 11.01.31.gif

4.1 为工作流设置图标

进入工作流 -> 双击工作流 -> 将图标拖拽进来即可

Kapture 2022-07-06 at 10.21.23.gif

4.1 为工作流新增一个可执行脚本

进入工作流配置页面 -> 选择要配置的工作流 -> 在编辑区右键 -> Inputs -> Script Filter -> 填写信息

  1. keyword: 工作流关键字, 用于唤醒该工作流
  2. placeholder Title: 标题
  3. placeholder Subtext: 子标题
  4. please Wait Subtext: 脚本执行过程中需要显示的文案
  5. 可执行脚本内容: ./node_modules/.bin/run-node index.js 调用项目中 run-node 命令, 执行 index.js 脚本文件
  6. 注意: Script Filter 因为不需要参数, 所以记得参数设置为 No Argument

Kapture 2022-07-06 at 10.51.28.gif

4.2 新建文件 index.js

项目根目录下新增文件 index.js 通过 console.log 打印出如下 JSON 字符串 JSON 格式参考: www.alfredapp.com/help/workfl…

console.log(JSON.stringify({
  items: [
    {
      "title": "掘金", // 标题
      "subtitle": "打开掘金", // 子标题
      "arg": "https://juejin.cn", // 返回参数(链接), 供后面流程使用
      // "icon": "./juejin.png", 图标, 可选, 默认为工作流设置的图标

      // 定制键盘按键的方法
      "mods": { 
        // 按 alt (option) 后将替换上面的配置
        "alt": { 
          "subtitle": "打开活动中心",
          "arg": "https://juejin.cn/events/all",
        },
        // 按 cmd 后将替换上面的配置
        "cmd": {
          "subtitle": "打开资讯",
          "arg": "https://juejin.cn/news",
        },
      },
    },
  ]
}))

4.3 测试

唤醒 Alfred -> 输入关键词 share -> 来回按 alt (option)cmd 观察下拉项 Subtext 的变化

Kapture 2022-07-06 at 11.01.31.gif

4.4 设置 Action

在上面我们完成了一个简单的工作流, 并成功唤醒, 但是当我们选中下拉项时, 是不会触发任何操作的; 接下来我们需要为我们的工作流设置一个 Action:

  1. 当我们直接选中下拉项, 我们需要打开掘金首页
  2. 当我们按住 alt (option) 选中下拉项, 我们需要打开掘金活动中心
  3. 当我们按住 cmd 选中下拉项, 我们需要打开掘金资讯中心
  1. 进入工作流配置页面 -> 选择要配置的工作流 -> 在编辑区右键 -> Actions -> Open URL -> Save
  2. 将工作流的两个步骤关联起来

Kapture 2022-07-06 at 11.21.51.gif

4.4 测试

唤醒 Alfred -> 输入关键词 share

  1. 直接选中下列项, 测试是否能够打开掘金首页
  2. 按住 alt (option) 选中下拉项, 测试是否能够打开掘金活动中心
  3. 按住 cmd 选中下拉项, 测试是否能够打开掘金资讯中心

Kapture 2022-07-06 at 11.32.27.gif

4.5 使用 alfy.output 输出

在上文 index.js 中我们是通过 console.log 输出了 JSON 字符串 该 JSON作为 下拉项配置 进行输出 实际上 alfy 提供了 output 方法, 我们只需要调用该方法并传入 items 数组即可

import alfy from 'alfy'

alfy.output([
  {
    "title": "掘金", // 标题
    "subtitle": "打开掘金", // 子标题
    "arg": "https://juejin.cn", // 返回参数(链接), 供后面流程使用
    // "icon": "./juejin.png", 图标, 可选, 默认为工作流设置的图标

    // 定制键盘按键的方法
    "mods": { 
      // 按 alt(option) 后将替换上面的配置
      "alt": { 
        "subtitle": "打开活动中心",
        "arg": "https://juejin.cn/events/all",
      },
      // 按 cmd 后将替换上面的配置
      "cmd": {
        "subtitle": "打开资讯",
        "arg": "https://juejin.cn/news",
      },
    },
  },
])

五、进入正题

5.1 修改图标

将上文设置的掘金图标替换为 Antd 图标

Kapture 2022-07-06 at 22.21.57.gif

5.2 获取 Antd 文档(组件)列表

首先我们第一步是需要知道 Antd 官网 总共有哪些组件, 以及它们对应文档的链接, 这里就有两个方案:

  1. 方案一就是代码里通过配置文件直接写死, 但是呢这样显然不够 "高级"、"智能"
  2. 方案二就是通过读取 Antd 官网 解析 hmtl 拿到所有组件名以及它们对应的链接并自动生成一份配置

这里我们选择方案二, 具体流程如下:

  1. 首先需要安装下 jsdom 依赖, 我们需要用它来解析 html
npm i jsdom
  1. 创建文件 updateConfig.js 开始我们的编码
import fs from 'fs'
import util from 'util'
import process from 'child_process'
import { JSDOM } from 'jsdom'

const exec = util.promisify(process.exec);
const PAGE_URL = 'https://ant.design/components/overview-cn/'

// 1. GET 请求拿到官网 HTML
const { stdout: body } = await exec(`curl ${PAGE_URL}`)

// 2. 使用 jsdom 解析 HTML
const { window } = await new JSDOM(body);

// 3. 获取所有菜单节点, 拿到我们想要的数据
const res = [...window.document.querySelectorAll('.main-menu-inner a')].map(ele => {
  const [first, second] = ele.querySelectorAll('span');
  return {
    arg: `https://ant.design/${ele.getAttribute('href')}`,
    title: first?.textContent? `${first?.textContent} ${second?.textContent}`: ele.textContent,
  }
})

// 4. 生成配置文件 config.js
fs.writeFileSync('./config.js', `export default ${JSON.stringify(res, null, 4)}`)

// 5. 输出日志
console.log('更新配置成功!')
  1. 测试: 执行代码, 看是否生成配置文件
node ./updateConfig.js
  1. 这里我们并不希望 git 上传生成的配置文件 config.js, 因为我们希望每次 安装工作流的同时, 都能够生成最新的配置, 并支持手动更新 (这个后面会进行详细讲解), 这里就需要修改下 .gitignore 文件
node_modules
+ config.js
  1. 修改 package.json 并每次 安装工作流 同时自动更新配置
"scripts": {
+   "start": "node ./updateConfig.js && alfy-init",
    "clear": "alfy-cleanup",
    "postinstall": "npm run start"
},
  1. 测试: 卸载并重新安装工作流, 测试是否成功安装工作流、并生成配置文件 (为了方便测试, 如果你项目下有配置文件请一并删除)
rm ./config.js # 删除配置文件
npm run clear # 卸载工作流
npm run start # 安装工作流

5.3 手动更新配置文件

倘若我们已经安装完工作流, 但是呢, 官网添加了新的组件, 这时候就有了手动更新配置的需求, 所以我们希望能够通过 Alfred 并输入关键词 updateShareConfig 来完成配置的更新

完整的工作流流程如下

  1. Inputs / Keyword: 关键词, 用于唤醒工作流
  2. Actions/Run Script: 执行脚本, 执行脚本 updateConfig.js
  3. Outputs/Post Notification: 消息提示, 更新成功后会有消息提示

image.png

Inputs/Keyword 配置如下:

  1. keyword: 关键词, 这里其实不区分大小写
  2. Title: 显示标题
  3. Subtext: 子标题
  4. 注意: 因为不需要参数, 所以记得参数设置为 No Argument

image.png

Actions/Run Script 配置如下: Script: 脚本,./node_modules/.bin/run-node updateConfig.js 脚本调用项目中 run-node 命令, 执行 updateConfig.js

image.png

Outputs/Post Notification 配置有:

  1. Title: 提示信息的标题
  2. Text: 提示信息的文本内容, {query} 为上一个流程输出的内容, 也就是执行脚本时, 脚本打印的内容, 即 updateConfig.js 最后 console.log 输出的日志

image.png

  1. 测试: 删除项目中生成的配置文件 config.js -> 唤醒 Alfred -> 输入关键词 updateShareConfig -> 选择下拉项, 查看是否生成配置文件、是否有消息提示

Kapture 2022-07-06 at 21.43.28.gif

5.3 获取参数

我们需要有个筛选的功能, 故而第一步我们需要先获取到参数

修改 Script Filter:

  1. 修改脚本内容, 可通过 "$1"向脚本传递参数
  2. 修改参数模式为 Argument Optional

Kapture 2022-07-06 at 23.05.53.gif

修改 index.js 尝试让下拉项标题实时展示我们输入的内容 **注意: **同 node 脚本, process.argv[2] 开始为执行脚本时输入的参数

import alfy from 'alfy'

alfy.output([
  {
    "title": process.argv[2] || '请输入文本', // 标题
    "subtitle": "打开掘金", // 子标题
    "arg": "https://juejin.cn", // 返回参数(链接), 供后面流程使用
  },
])

**测试: **唤醒 Alfred-> 输入关键词 share -> 随意输入文本 -> 观察下拉项标题的变化

Kapture 2022-07-06 at 23.14.47.gif

5.4 基础功能实现

修改 index.js

  1. 读取配置文件
  2. 根据「检索参数」进行过滤
  3. 处理过滤后数据: 过滤后数据为空, 则返回全部、并追加额外字段 subtitle
  4. 输出下拉项
import alfy from 'alfy'

// 1. 读取配置文件
import config from './config.js'

// 2. 根据「检索参数」进行过滤
const filterData = config
  .filter(v => v.title.toLocaleLowerCase().includes(process.argv[2] || ''))
  
// 3. 处理过滤后数据: 过滤后数据为空, 则返回全部、追加额外字段 subtitle
const items = (filterData.length !== 0 ? filterData : config)
  .map(v => ({
    ...v,
    subtitle: `打开组件: ${v.title}`
  }))

// 4. 输出下拉项
alfy.output(items)

测试: 唤醒 Alfred -> 输入关键词 share -> 进行搜索 -> 选中下拉项

Kapture 2022-07-07 at 10.34.45.gif

5.4 按住 com 打开 API

上文基本已经实现我们想要的功能, 但是我们大部分的场景可能是想要直接跳转到组件的 API 模块 , 下面我们来实现这么一个功能, 当我们按住 cmd 的同时选择下拉项, 将直接跳转到对应组件的 API模块

import alfy from 'alfy'

// 1. 读取配置文件
import config from './config.js'

// 2. 根据「检索参数」进行过滤
const filterData = config
  .filter(v => v.title.toLocaleLowerCase().includes(process.argv[2] || ''))
  
// 3. 处理过滤后数据: 过滤后数据为空, 则返回全部、追加额外字段 subtitle
const items = (filterData.length !== 0 ? filterData : config)
  .map(v => ({
    ...v,
+   subtitle: `打开组件: ${v.title}, 按住 cmd 打开 API`,
+   // mod 可以设置通过控制不同按键, 来修改覆盖默认参数
+   mods: {
+     cmd: {
+       arg: `${v.arg}#api`,
+       subtitle: `打开组件: ${v.title}, 并跳转至 API`,
+     },
+   },
  }))

// 4. 输出下拉项
alfy.output(items)

到此我们所有功能都已经实现, 下面是完整的功能演示

Kapture 2023-02-06 at 16.36.58.gif

六、发布

最后的最后我们可以将我们的包上传到 NPM , 供别人下载使用

# 在这之前你可能还需要注册、登录
npm publish

测试: 卸载工作流 -> 全局安装发布的 NPM 包 -> 测试是否正常使用

npm run clear  # 卸载工作流
npm i alfred-share -g # 全局安装我们发布的 npm 包

如果需要卸载工作流, 只需要卸载全局安装的包即可

npm uninstall alfred-share -g

根据 NPM 规定, 发布的包在 24 小时内是允许进行撤销的, 对于测试包, 为了不占用资源最后还是尽可能的进行撤销

npm unpublish --force

七、参考资料

  1. Alfred 官网
  2. alfy
  3. jsdom
  4. alfred-antd
  5. npm 包的发布与删除

八、Q & A

7.1 Alfred 如果关闭预览: 按 Shift 默认会打开预览弹窗

修改 Features-> Previews 里设置即可

7.2 Outputs Post Notification 无法发起通知

需要注意消息通知那儿是否禁止 Alfred 进行消息提示

Group 3143