簡介
有時間研究下開源庫的源碼,總是會有些收獲的。注意到 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 */
參考文章
原文釋出時間為:2017/11/28
原文作者: ios122
本文來源:
開源中國如需轉載請聯系原作者