怎麼用 Node.js 輔助 imgcook 自動產生依賴?以下這篇文章就來跟大家介紹一下生成方法,有一定的參考價值,希望對大家有幫助!
imgcook 在淘寶內部版本提供了類似依賴管理的功能,用於在imgcook 編輯器編寫函數時引入其他依賴包,例如axios、underscore、@rax/ video 等。
不過從使用體驗上,還是較為繁瑣,因為編輯器並沒有讓大家形成像在package.json 宣告依賴的習慣,而且因為編輯器是GUI 介面,所以從每個函數打開程式碼,並查看依賴的操作是比較繁瑣的,這就導致每次開發完一個imgcook 模組後,如果依賴了其他包(大部分情況下都需要),就需要一個個打開函數並確認版本號,並在依賴管理中添加,這在我使用的過程中,往往是一次痛苦的過程。
imgcook 提供了Schema 原始碼開發模式,透過在編輯器中直接修改模組協定(Schema)就能取代GUI 的操作步驟,然後透過搜尋dependencies,我發現依賴管理功能是透過協定中的imgcook.dependencies
實現的:
{ "alias": "Axios", "packageRax1": "axios", "versionRax1": "^0.24.0", "packageRaxEagle": "axios", "versionRaxEagle": "^0.24.0", "checkDepence": true }
由於函數的程式碼也存在協定中,那麼是不是只需透過處理原始協議文檔,掃描對應的依賴並保存到節點中,再點擊「儲存」就可以看到依賴管理中的套件列表被更新了。
為此我在@imgcook/cli 中實作了拉取模組協定內容的功能,具體Pull Request 有: imgcook/imgcook-cli#12 和imgcook/imgcook-cli#15,可以透過命令列工具拉取對應模組的協定(Schema)如下:
$ imgcook pull <id> -o json
執行後會把模組協定內容輸出到命令列中的stdout。
有了這個功能後,就可以實作一些命令列工具,基於Unix Pipeline 程序,與imgcook-cli 的資料來源形成協作,舉個例子,透過imgcook pull 輸出的JSON 並不易讀,那麼不妨寫一個imgcook-prettyprint 來美化輸出結果,程式碼實作如下:
#!/usr/bin/env node let originJson = ''; process.stdin.on('data', (buf) => { originJson += buf.toString('utf8'); }); process.stdin.on('end', () => { const origin = JSON.parse(originJson); console.log(JSON.stringify(origin, null, 2)); });
上面的程式透過process.stdin
接收管線(Pipeline)上游的數據,即imgcook 模組協定內容,然後在end
事件中解析並美化輸出,執行以下指令:
$ imgcook pull <id> -o json | imgcook-prettyprint
就能看到美化後的輸出結果了,這就是一個Unix Pipeline 程式的簡單範例。
接下來就來看下如何透過這種方式,完成依賴的自動產生。與上面範例類似,我們再建立一個檔案ckdeps:
#!/usr/bin/env node let originJson = ''; process.stdin.on('data', (buf) => { originJson += buf.toString('utf8'); }); process.stdin.on('end', () => { transform(); }); async function transform() { const origin = JSON.parse(originJson); const funcs = origin.imgcook?.functions || []; if (funcs.length === 0) { process.stdout.write(originJson); return; } console.log(JSON.stringify(origin)); }
透過origin.imgcook.functions
,可以取得到函數的程式碼內容,例如:
{ "content": "export default function mounted() {\n\n}", "name": "mounted", "type": "lifeCycles" }
那麼接下來就是透過解析content
,取得到程式碼中的import 語句,再產生對應的依賴物件到origin.imgcook.dependencies
中,那麼我們需要引用@swc/core 來解析JavaScript 程式碼:
const swc = require('@swc/core'); await Promise.all(funcs.map(async ({ content }) => { const ast = await swc.parse(content); // the module AST(Abstract Syntax Tree) }));
取得到ast 後,就能透過程式碼取得import 語句的資訊了,不過由於ast 比較複雜,@swc/core 提供了專用的遍歷機制如下:
const { Visitor } = require('@swc/core/visitor'); /** * 用于保存通过函数解析, 获得的依赖对象列表 */ const liveDependencies = []; /** * 定义访问器 */ class ImportsExtractor extends Visitor { visitImportDeclaration(node) { let alias = 'Default'; liveDependencies.push({ alias, packageRax1: node.source.value, versionRax1: '', packageRaxEagle: node.source.value, versionRaxEagle: '', checkDepence: true, }); return node; } } // 使用方式 const importsExtractor = new ImportsExtractor(); importsExtractor.visitModule(ast);
類別ImportsExtractor
繼承自@swc/core/visitor 的Visitor
,由於是要遍歷import 宣告語句,它的語法型別名稱是ImportDeclaration
,因此只需要實作visitImportDeclaration(node)
方法,即可在方法內取得所有的import 語句,再依照對應節點的結構轉換成依賴物件並更新就好。定義好抽取器後,剩下的事情就是把 ast 餵給抽取器,這樣就能把模組所有的依賴都收集起來,用於後面依賴的生成。
從上面的程式碼可以看到,版本號目前使用了空字串,這會導致我們如果更新協定內容,依賴的版本資訊會遺失,因此我們需要定義一種取得版本的方法。
由於前端的依賴都是儲存在 NPM Registry 上的,因此我們可以透過 HTTP 介面來取得版本,例如:
const axios = require('axios'); async function fillVersions(dep) { const pkgJson = await axios.get(`https://registry.npmjs.org/${dep.packageRax1}`, { type: 'json' }); if (pkgJson.data['dist-tags']) { const latestVersion = pkgJson.data['dist-tags'].latest; dep.versionRax1 = `^${latestVersion}`; dep.versionRaxEagle = `^${latestVersion}`; } return dep; }
我们按照 https://registry.npmjs.org/${packageName}
的规则,就能拿到存储在 Registry 中的包信息,然后 data['dist-tags'].latest
代表的是 latest 标签对应的版本,简单来说就是当前包的最新版本,然后再基于这个版本号增加一个 ^
版本前缀即可(你也可以按照自己的诉求修改最终的版本以及 NPM Registry)。
最后一步,就是把我们从函数代码中抓取的依赖信息更新,并输出出来:
async function transform() { // ... origin.imgcook.dependencies = newDeps; console.log(JSON.stringify(origin)); }
然后通过运行:
$ imgcook pull <id> -o json | ckdeps > { ..., "dependencies": [{ ...}] }
然后,开发者只需要把输出的 JSON 拷贝到编辑器中保存。哦,等等,在编辑器中并不能直接使用 JSON 保存,而是需要使用 ECMAScript Module 的方式(export default { ... }
),那这样是不是意味着每次都需要手动编辑一下呢,答案是否,Unix Pipeline 的思路非常利于解决这种流程问题,我们只需要再新建一个节点脚本 imgcook-save 即可:
#!/usr/bin/env node let originJson = ''; process.stdin.on('data', (buf) => { originJson += buf.toString('utf8'); }); process.stdin.on('end', () => { transform(); }); async function transform() { const origin = JSON.parse(originJson); console.log(`export default ${JSON.stringify(origin, null, 2)}`); }
最后完整的命令是:
$ imgcook pull <id> -o json | ckdeps | imgcook-save > export default { ... }
这样,我们就可以直接拷贝内容到编辑器。
比如,我在喔其中一个项目的 created 函数中增加了 axios 的依赖,关闭窗口后点击保存(确保 Schema 保存),然后通过命令:
$ imgcook pull <id> -o json | ckdeps -f | imgcook-save
然后在编辑器中打开 Schema 编辑,复制生成的内容并保存,然后打开“依赖管理”可以看到:
通过解析生成的代码已经更新到依赖面板了,这下终于可以解放双手,去做其他的事情了。
是不是这样就结束了呢?在 macOS 中,提供了 pbcopy 命令,可以复制 stdin 到剪贴板,那么跟 imgcook 的例子结合一下:
$ imgcook pull <id> -o json | ckdeps | imgcook-save | pbcopy
这样就省掉了自己拷贝的工作,命令执行完直接打开编辑器 ⌘V
即可。
最后的最后,我要升华一下主题,在 @imgcook/cli 支持了输出 JSON 文本的功能后,就意味着 imgcook 接入了 Unix Pipeline 的生态,通过这种方式,我们可以在这个过程中构建很多有趣实用的工具,并与很多 Unix 工具协作使用(比如 bpcopy、grep、cat、sort 等)。
本文只是通过依赖的自动生成为例,使用 Unix Pipeline 的方式,验证了其可行性以及和 imgcook 编辑器配合使用的体验,目前来说,我可以通过这种方式,弥补不少编辑器上的体验缺失,让我更方便地使用 imgcook 的核心功能。
更多node相关知识,请访问:nodejs 教程!!
以上是淺析Node.js + imgcook怎麼自動產生依賴的詳細內容。更多資訊請關注PHP中文網其他相關文章!