【smart-transform】取自 Atom 的 babeljs/coffeescript/typescript 智能轉 es5 庫 原 薦

簡介

有時間研究下開源庫的源碼,總是會有些收穫的。注意到 Atom 插件編寫時,可以直接使用 babel, coffeescript 或者 typescript。有些詫異,畢竟 Electron 中內置的 node 引擎,也一定不是完全兼容 es6,更不用說 coffeescript 和 typescript了。所以,必然在加載插件時,Atom 有某種自動轉換的操作。剛好最近有一些類似的需求,需要批量以單個文件的方式轉換一些其他語法的文件到 es5 兼容的js文件,於是就把 Atom 的轉換機制拆分了出來,寫成一個 cli。

他山之玉,不敢私藏。如果只是使用,請直接在npmjs上查找:smart-transform

特色定製

毋容置疑,最核心的地方是取自於 Atom 本身。之所以把這個邏輯單獨剝離出來,主要是我很羨慕 Atom 插件編寫時,各種語法隨心使用的舒爽!要是自己項目,也能這麼隨意,豈不是爽歪歪!!!

爲了獨立於 Atom 使用,同時又具備一定的通用新,主要定製性體現在:

  • 將邏輯剝離成一個 cli 命令行工具,以後不管自己還是別人,拿來即用。不是每個前端,都很擅長 nodejs,所以我覺得,這還是能方便一些人的。
  • 通過配置文件,允許個性化定製。即,每個項目的輸入和輸出目錄可以通過配置文件來自由配置。現在還不夠靈活,只支持指定唯一一個輸入文件夾和唯一一個輸出文件夾,不過暫時夠用了。
  • 引入 uglify-js 進行壓縮和混淆。這一點,確實是項目本身的需要,我相信大部分人,都有這個需求吧?另外,之所以直接使用 uglify-js ,當然是因爲我不想再額外配置 webpack 呀!!

扔一個 smart-transform.json 配置文件示例上來吧:

{
  "in":"./src",
  "out":"./lib",
  "exclude":["./src/hi-ignore.js"],
  "minify":true,
  "minifyExclude":["./src/hi-ts.ts"]
}

源碼解讀

package.json

  "bin": {
    "smart-transform": "index.js"
  }

比較特殊的是 bin 字段。第一次寫 cli 的童鞋,常常因爲沒有寫這個字段,導致沒有以全局命令的形式使用自己的工具庫。

index.js

這是定製最多的一個文件。它實現的主要功能是,讀取具體項目根目錄的配置文件 smart-transform.json ,然後根據內部字段,來進行一些個性化的轉換操作。

目前支持的操作有:

  • 將指定目錄的 babeljs/coffeescript/typescript 轉爲 es5 兼容的js文件,並輸出到另一個目錄。
  • 忽略某些文件,不對其進行轉換操作。
  • 轉換時,可選支持同時進行壓縮和混淆操作。壓縮和混淆,目前使用的是 uglify-js

代碼不長,但是本身有一些 node 相關的代碼,所以我就還是貼出來,感興趣的順便瞅一眼:

#!/usr/bin/env node
'use strict'
var path = require("path")
var fs = require ('fs-plus')
var fse = require('fs-extra')
var os = require("os")
var {execSync} = require("child_process")
var UglifyJS = require("uglify-js")

var argv = require('minimist')(process.argv.slice(2))

var project = argv.project
var configInfo = require(path.resolve(project,"./smart-transform.json"))

var inDir = path.resolve(project,configInfo.in)
var outDir = path.resolve(project,configInfo.out)
var minify = configInfo.minify

var excludeFiles = configInfo.exclude.map(function (filePath) {
  return path.resolve(project,filePath)
})

var minifyExcludeFiles = configInfo.minifyExclude.map(
  function (filePath) {
    return path.resolve(project,filePath)
  }
)

fse.ensureDirSync(outDir)

var inFiles = fs.listSync(inDir,[".js",".ts","coffee"])

for (var inFile of inFiles) {
    if (excludeFiles.includes(inFile)) { // 不需要處理的,直接複製到輸出目錄
      var outFile = path.resolve(project,outDir,path.basename(inFile))
      fse.copySync(inFile,outFile)
      continue
    }

    var sourceCode = require("./compile-file")(inFile)

    if (minify && !minifyExcludeFiles.includes(inFile)) {
      sourceCode = UglifyJS.minify(sourceCode).code
    }

    var outFile = path.resolve(project,outDir,path.basename(inFile,path.extname(inFile)) + ".js")
    fse.ensureFileSync(outFile)
    fs.writeFileSync(outFile,sourceCode)
}

compile-file.js

相關預編譯邏輯取自原Atom代碼中的 src/compile-cache.js 類,主要區別是,禁用代碼地圖並禁用輸出代碼內的註釋。考慮到項目本身的內部兼容性,並沒有直接使用最新版的 Atom 源碼演繹。如果自己有其他定製需求,可以直接看 Atom 源碼。

這個文件比較出彩的地方是,它把各種類似的語法都使用 COMPILERS 的機制管理。一種語法對應一個 COMPILER。在某些特定情況下,如果你想解析或轉換其他類型的文件,只需要修改這個類,新增一個 COMPILER 即可。

'use strict'
var path = require('path')
var fs = require('fs-plus')

var COMPILERS = {
  '.js': require('./babel'),
  '.ts': require('./typescript'),
  '.coffee': require('./coffee-script')
}

function compileFileAtPath (filePath) {
  const extension = path.extname(filePath)
  const compiler = COMPILERS[extension]

  var sourceCode = fs.readFileSync(filePath, 'utf8')

  if (compiler.shouldCompile(sourceCode, filePath)) {
    const compiledCode = compiler.compile(sourceCode, filePath)
    return compiledCode
  }

  return sourceCode
}

module.exports = compileFileAtPath

babel.js coffee-script.js typescript.js

分別取自 Atom 源碼中的 babel.js coffee-script.js typescript.js。有極小的修改,典型的 拿來主義 。有興趣的,直接去看下源碼,此處不做贅述。

注意

使用 bable 的js文件,開頭應是以下幾種的其中一種,否則無法被識別:

/** @babel */
"use babel"
'use babel'
/* @flow */

參考文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章