【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 */

参考文章

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容

  • 作者:王子亭 Atom 是 GitHub 在 2014 年发布的一款基于 Web 技术构建的文本编辑器,我从 20...
    LeanCloud官方帐号阅读 2,950评论 0 13
  • 感赏女儿最近女儿情绪稳定,感赏女儿每天回家早了,比以前开始作业时间早了,能够自觉睡觉起床了,感赏女儿休息时间爱跳舞...
    徐菲儿阅读 168评论 1 0
  • 终于摆脱了“每逢佳节胖三斤”的魔咒,可是不料“节后焦虑症”突然来袭。 正月初八左右,上班族也基本上回归职场,开始新...
    充满烟火气的女王阅读 1,336评论 6 20
  • 情不知所起 一往而深 生者可以死 死可以生 生而不可与死 死而不可复生者 皆非情之至也
    Faizah阅读 1,287评论 0 0