
Some script【免費(fèi)下載鏈接】zxA tool for writing better scripts項(xiàng)目地址: https://gitcode.com/GitHub_Trending/zx/zxls— is an unix command to get directory contents. Lets see how to use it inzx:// ts, js, cjs, mjs, etc const {stdout} await $ls -l console.log(directory contents:, stdout)This part invokes the same command in a different way:# bash syntax ls -l執(zhí)行方式就是一行命令 bash zx script.md 在 [src/cli.ts](https://link.gitcode.com/i/5d647a6721c5d3526b80ce7516315880) 的 readScript() 中可以看到觸發(fā)點(diǎn)當(dāng)腳本擴(kuò)展名是 .md 時(shí)先調(diào)用 transformMarkdown(script) 轉(zhuǎn)換內(nèi)容然后 tempPath getFilepath(dir, base, EXT) 生成一個(gè)臨時(shí) .mjs 文件見(jiàn) [src/cli.ts#L238-L241](https://link.gitcode.com/i/5d647a6721c5d3526b80ce7516315880#L238-L241)轉(zhuǎn)換后的代碼寫(xiě)入該臨時(shí)文件后被 import() 動(dòng)態(tài)加載。 ## 逐塊解析測(cè)試夾具 test/fixtures/markdown.md [test/fixtures/markdown.md](https://link.gitcode.com/i/48c2530950b90b179e053e9a1d0a78d8) 是 CI 中反復(fù)運(yùn)行的 Markdown 腳本它幾乎覆蓋了所有轉(zhuǎn)換分支正文注釋、引用塊、js 塊、tilde 圍欄、bash 塊、未知語(yǔ)言塊、縮進(jìn)代碼塊。逐塊看它的“命運(yùn)” ### 1. 第一行 ignore正文自動(dòng)變成注釋 markdown # Markdown ignore 夾具開(kāi)頭的 # Markdown 標(biāo)題和 ignore 這樣的普通行在轉(zhuǎn)換后都變成 // # Markdown、// ignore 形式的注釋行。transformMarkdown() 的 root 狀態(tài)對(duì)圍欄之外的行統(tǒng)一執(zhí)行 out.push(// line)見(jiàn) [src/md.ts#L69-L71](https://link.gitcode.com/i/98ab81ca97a872aa9dbf9a46db45a863)空行也變成 // 。這意味著 Markdown 里任何散文都可以自由書(shū)寫(xiě)永遠(yuǎn)不會(huì)被執(zhí)行——寫(xiě)腳本時(shí)可以放心地用標(biāo)題、表格、引用做文檔。 ### 2. 引用塊里的代碼因?yàn)? 前綴而不被識(shí)別為圍欄 markdown echo ignore 這段引用塊看起來(lái)是一個(gè)代碼圍欄但每行都帶 前綴。圍欄識(shí)別正則 fenceRe 要求圍欄前最多 3 個(gè)空格(?indent {0,3})(?fence({3,20}|~{3,20}))...見(jiàn) [src/md.ts#L21-L22](https://link.gitcode.com/i/677dc746334bd2c170bb543b72163510) 不滿足這個(gè)條件因此這五行全部落入 root 狀態(tài)被注釋掉echo ignore 永遠(yuǎn)不會(huì)運(yùn)行。[test/cli.test.js#L93-L98](https://link.gitcode.com/i/9f7ed2cb6b6dd271a861e30ce0ae339c) 的 --quiet 測(cè)試正驗(yàn)證了這一點(diǎn)運(yùn)行 node build/cli.js --quiet test/fixtures/markdown.md 后stderr 中不出現(xiàn) ignore——如果引用塊被誤執(zhí)行zx 的命令回顯會(huì)把 echo ignore 打到 stderr 上。 ### 3. js 與 ~~~js 圍欄按原始代碼執(zhí)行 js await $whoami await $echo ${__dirname} ~~~js await $echo tilde ~~~ 夾具中同時(shí)出現(xiàn)了反引號(hào)圍欄和 tilde 圍欄兩種寫(xiě)法這對(duì)應(yīng) fenceRe 中 ({3,20}|~{3,20} ) 的分支。對(duì) js 系代碼塊轉(zhuǎn)換器不添加任何前綴代碼原樣進(jìn)入輸出linePrefix 見(jiàn) [src/md.ts#L44-L47](https://link.gitcode.com/i/0c175eeca9b6e2de365352cf168d1a44)因此模板字符串里的 ${__dirname} 會(huì)在 Node 運(yùn)行時(shí)求值。注意 __dirname 不是 zx 魔法變量而是 injectGlobalRequire() 注入的全局變量見(jiàn)下文。 ### 4. bash 圍欄整塊包進(jìn) await $... bash VAR$(echo hello) echo $VAR bash/sh/shell 代碼塊的處理與 js 塊不同轉(zhuǎn)換器在塊首輸出 await $ 、塊尾輸出反引號(hào)closeOut 見(jiàn) [src/md.ts#L48-L51](https://link.gitcode.com/i/37fb82afbb12b0d4e210f6c66df9a4fc)整塊 shell 語(yǔ)句作為一個(gè)模板字符串交給 zx 的 $ 執(zhí)行。多行 shell 語(yǔ)法如這里的變量賦值與展開(kāi)因此被完整保留。test/md.test.ts 中的斷言展示了轉(zhuǎn)換結(jié)果~~~sh\necho foo\n~~~ 變成 await $\\necho foo\n\見(jiàn) [test/md.test.ts#L77-L80](https://link.gitcode.com/i/ca95bea1eca7dacde54c9ea40f44a159)。 ### 5. 動(dòng)態(tài) import 與 __filename 夾具后兩個(gè) js 塊 js console.log(chalk.yellowBright(__filename)) js await import(chalk) 它們演示了 Markdown 腳本中可以使用動(dòng)態(tài) import 引入依賴[docs/markdown.md](https://link.gitcode.com/i/b4d1895487886ea6772e230afa098c19) 明確說(shuō) “You can use imports here as well”以及 __filename 指向 Markdown 文件所在位置。[docs/markdown.md](https://link.gitcode.com/i/b4d1895487886ea6772e230afa098c19) 專門(mén)說(shuō)明 “The __filename will be pointed to **markdown.md**”。從源碼結(jié)構(gòu)看機(jī)制是.md 腳本轉(zhuǎn)換后寫(xiě)入與原文件同目錄、同名主基的臨時(shí) .mjs 文件[src/cli.ts#L238-L241](https://link.gitcode.com/i/5d647a6721c5d3526b80ce7516315880#L238-L241)隨后 injectGlobalRequire(scriptPath) 用該路徑設(shè)置全局 __filename、__dirname 和 require見(jiàn) [src/cli.ts#L261-L266](https://link.gitcode.com/i/5d647a6721c5d3526b80ce7516315880#L261-L266)。所以?shī)A具里 await $echo ${__dirname} 打印出來(lái)的正是原 Markdown 文件所在的目錄。 ### 6. 縮進(jìn)代碼塊作為“遺留行為”原樣執(zhí)行 markdown // ignore console.log(world) 夾具第 32–33 行是縮進(jìn) 4 個(gè)空格的代碼。Markdown 語(yǔ)義上這算“縮進(jìn)代碼塊”但 zx 的轉(zhuǎn)換器把“空白行之后、以兩個(gè)以上空格或 Tab 開(kāi)頭”的行視為可執(zhí)行代碼保留原樣輸出tab 狀態(tài)見(jiàn) [src/md.ts#L63-L82](https://link.gitcode.com/i/b5cd63c65df7c26fc433d86e8d19a9a4)。因此 console.log(world) 會(huì)被執(zhí)行并在 stdout 打印 world——--quiet 測(cè)試中 assert.ok(p.stdout.includes(world))[test/cli.test.js#L97](https://link.gitcode.com/i/a2bcc986a875747d041aee115ca2d7fb)驗(yàn)證的正是這條路徑。test/md.test.ts 也把這一行為命名為 “preserves tab-indented blocks after a blank line (legacy behavior)”。 ### 7. 未知語(yǔ)言圍欄整體注釋 markdown Other code blocks are ignored: css .ignore {} 對(duì)既不是 js 系也不是 bash 系的圍欄此處是 css轉(zhuǎn)換器把塊內(nèi)每行加上 // 前綴linePrefix // 圍欄本身不產(chǎn)生任何輸出。這與 [docs/markdown.md](https://link.gitcode.com/i/b4d1895487886ea6772e230afa098c19) “Other kinds are ignored” 的說(shuō)明一致test/md.test.ts 中 ~~~\nunknown code block\n~~~ 的斷言給出了同樣的轉(zhuǎn)換結(jié)果。 ## 底層原理transformMarkdown 的三態(tài)狀態(tài)機(jī) 以上每一塊的命運(yùn)都由 [src/md.ts](https://link.gitcode.com/i/bd674b21e0a18d628702fef8a821f6e4) 中 transformMarkdown() 的一個(gè)逐行狀態(tài)機(jī)決定狀態(tài)只有三種 - **root**處理圍欄外的內(nèi)容。每行匹配 fenceRe命中 js 組則把后續(xù)行原樣輸出命中 bash 組則用 await $ 包裹其余組全部注釋化未命中圍欄時(shí)行被加 // 前綴空行變 // 。 - **tab**root 狀態(tài)下若“前一行為空”且當(dāng)前行滿足 ^( |\t)進(jìn)入該狀態(tài)并原樣保留行內(nèi)容遇到空行保留空行遇到非縮進(jìn)行則注釋化并回到 root。 - **fence**圍欄內(nèi)部。用 endRe與開(kāi)圍欄同字符、長(zhǎng)度不小于開(kāi)圍欄、前綴最多 3 個(gè)空格判斷結(jié)束行內(nèi)部行按 stripRe 去除與開(kāi)圍欄相同的縮進(jìn)后輸出。CommonMark 允許圍欄縮進(jìn) 3 個(gè)空格test/md.test.ts 中 “accepts fences indented up to 3 spaces” 的測(cè)試用三級(jí)列表嵌套圍欄驗(yàn)證了這一點(diǎn)。 另外兩個(gè)工程細(xì)節(jié)值得注意 - **換行符處理**文件按 /\r\n|[\n\r\u2028\u2029]/ 切分[src/md.ts#L35](https://link.gitcode.com/i/bd674b21e0a18d628702fef8a821f6e4#L35)覆蓋 CRLF、CR 以及 ES 的行分隔符對(duì)應(yīng)夾具 [test/fixtures/markdown-crlf.md](https://link.gitcode.com/i/b91ff2e48c1b5baec3a0d913a93d5de6) 和 [test/cli.test.js#L347-L350](https://link.gitcode.com/i/b52eb128838d5e1c62669e965d685f0e) 的 CRLF 測(cè)試斷言輸出包含 Hello, world!以及 test/md.test.ts 中 “handles all ECMAScript line terminators” 的用例。 - **執(zhí)行鏈路**zx script.md → readScript() 判定 .md 擴(kuò)展名 → transformMarkdown() 轉(zhuǎn)換 → 寫(xiě)入臨時(shí) .mjsgetFilepath 會(huì)先探測(cè) name.ext、再 name-randomid.ext避免覆蓋已有文件見(jiàn) [src/cli.ts#L289-L298](https://link.gitcode.com/i/5d647a6721c5d3526b80ce7516315880#L289-L298)→ runScript() 中 injectGlobalRequire() 注入 __filename/__dirname/require 后 import() 該文件退出時(shí)清理臨時(shí)文件[src/cli.ts#L143-L174](https://link.gitcode.com/i/5d647a6721c5d3526b80ce7516315880#L143-L174)。除了本地文件stdin配合 --ext .md和 HTTP URL 拉取的腳本同樣會(huì)走這條 .md 轉(zhuǎn)換路徑[src/cli.ts#L204-L245](https://link.gitcode.com/i/5d647a6721c5d3526b80ce7516315880#L204-L245)。 ## 測(cè)試如何驗(yàn)證這份夾具 test/cli.test.js 中與該夾具相關(guān)的用例構(gòu)成端到端驗(yàn)證 js // test/cli.test.js test(markdown scripts are working, async () { await $node build/cli.js test/fixtures/markdown.md }) test(markdown scripts are working for CRLF, async () { const p await $node build/cli.js test/fixtures/markdown-crlf.md assert.ok(p.stdout.includes(Hello, world!)) }) test(markdown scripts from stdin with --ext .md, async () { const md # Test\n\njs\necho(md-stdin-ok)\n\n const p await $node build/cli.js --ext.md ${md} assert.match(p.stdout, /md-stdin-ok/) })【免費(fèi)下載鏈接】zxA tool for writing better scripts項(xiàng)目地址: https://gitcode.com/GitHub_Trending/zx/zx創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考