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