手寫一個 typescript 打包器

halo,你們好,我是 132,那個啥,俺又出來詐屍啦vue

此次帶來的是一個 ts 打包器的主要思路,最終實現代碼先放一下node

github.com/yisar/picop…webpack

歡迎 star 和 fork!git

背景

最近其實寫了幾個 typescript 的庫,可是打包一直困擾着我,一方面感受 tsc 比較好用,不想用 rollup,一邊 ts 很難打包多文件github

typescript 是能夠將多文件打包爲一個文件的,使用 --outputFlieweb

"compilerOptions": {
    "module": "amd",
    "outFile": "./dist/doux.js"
  }
複製代碼

這樣能夠將全部的 ts 文件打包成一個 js 文件,但比較遺憾,只支持 adm 和 systemjstypescript

而咱們寫庫,一般是 umd 格式的,給 typescript 的人發 issue,回覆基本上不會支持json

因此就萌生了本身寫一個打包器的想法小程序

開始

傳統的 js 打包器都是分析 AST 而後進行各類的修改,替換,而後根據不一樣的格式進行包裹,最終產生一個能用的 js 文件瀏覽器

ts 也不例外……和 babel 同樣,ts 官方提供了一組 TS Compiler API

看上去很是不錯,比 babel 好太多,主要是 babel 生態亂,想幹啥就要用各類第三方庫

首先,須要建立一個 ts 項目,順便進行類型檢查

let diagnostics = ts.getPreEmitDiagnostics(
  ts.createProgram([entryFile], {
    strict: true,
    target: ts.ScriptTarget.Latest,
    moduleResolution: ts.ModuleResolutionKind.NodeJs
  })
)

if (diagnostics.length) {
  diagnostics.forEach(d => console.log(d.messageText))
  error('Type check Error.')
}
複製代碼

首先,咱們大概是須要拿到一個文件的 AST,使用 createSource

const source = program.getSourceFile('fixtures/test.ts')

if (source) {
  ts.forEachChild(source, node => {
    if (ts.isFunctionDeclaration(node)) {
      console.log(node.name && node.name.text)
    }
  })
}
複製代碼

拿到 source,咱們能夠根據 kind 進行判斷是哪種代碼

能夠對應 AST viewer 來使用: TS AST viewer

而後通過一波操做後,須要從新生成 AST

const ast = ts.createVariableDeclarationList([
  ts.createVariableDeclaration(
    ts.createIdentifier('test'),
    ts.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
    ts.createLiteral('Hello!')
  )
], ts.NodeFlags.Const)

const printer = ts.createPrinter()
const code = printer.printNode(ts.EmitHint.Unspecified, ast, source)
console.log(code)
複製代碼

到此,整個思路就捋順了,不過咱們既然是多文件打包,因此還須要根據 import 找到源文件,而後再去檢查

case ImportDeclaration:
        let moduleSpecifier = node.moduleSpecifier.getText(source)
        let dep = JSON.parse(moduleSpecifier) as string
        let depPath: string
        if (dep.startsWith('.')) {
          depPath = localModulePath(dep, path)
        }
        pathQueue.push(depPath)
        break
複製代碼

找到這個文件,須要再 compile 一次

很好,差很少就是這樣啦

總結

  1. 關於 AST

總的來講,作 typescript compiler 遠比 js 有前途的多,主要是 js 太亂了真的

其實 AST 也有兩種,一種是藉助 babel,webpack,ts compiler 生成的 AST,這種的缺點是比較亂,單詞比較長

另外一種是本身作 AST 的生成,這種就至關於 jsx 插件了,最終能夠生成和 vdom 同樣乾淨的樹

我我的比較傾向於第二種,但………工做量比較大

  1. 關於轉譯

編譯階段能夠說無所不能,前有各類打包器,作各類的代碼打包和優化,後有國內各類小程序轉譯框架,如 taro,nanachi……

可是事實證實,什麼東西適合作什麼事情都是預約好了的

taro-next 使用了模擬瀏覽器 API 的方式,放棄編譯思路,轉投 runtime

svelte 使用編譯思路,一直被 vue 等各類吐槽

因此看我的選擇吧

相關文章
相關標籤/搜索