PantoJS文件轉(zhuǎn)換引擎
PantoJS 是一個(gè)極其靈活的文件轉(zhuǎn)換引擎,通常用于項(xiàng)目的構(gòu)建和編譯,特別是 Web 前端項(xiàng)目。
它比較類(lèi)似于 Grunt 或 Gulp,但更高效、強(qiáng)大和靈活。
核心特性
支持任意定義的拓?fù)錁?gòu)建流程
只要是你能定義出來(lái)的構(gòu)建流程,無(wú)論如何復(fù)雜,只要是合理的,Panto 都可以支持
支持遺留文件的收集
在選擇特定的文件類(lèi)型后,可以一次性訪(fǎng)問(wèn)到未被選擇的其它文件
保證對(duì)每個(gè)源文件最多讀取一次
對(duì)于同一個(gè)文件存在一個(gè)以上不同的處理流程,讀取也最只有一次
保證對(duì)于每個(gè)文件的同樣處理流程只有一次
盡最大努力避免重復(fù)工作
支持文件級(jí)別的精確緩存,最大程度上避免不必要的計(jì)算
不必重新構(gòu)建的文件,盡最大努力利用緩存
支持文件級(jí)別的精確增量構(gòu)建
高效重復(fù)構(gòu)建
與Grunt/Gulp相比
| Grunt | Gulp | Panto | |
|---|---|---|---|
| 流式任務(wù) | ? | ? | ? |
| 拓?fù)淞鞒?/td> | ? | ? | ? |
| 一次讀取 | ? | ? | ? |
| 精確緩存 | ? | ? | ? |
| 精確增量 | ? | ? | ? |
快速入門(mén)
與 Grunt 和 Gulp 一樣,Panto 也需要在項(xiàng)目根目錄下定義流程配置文件 pantofile.js,但不支持 coffeescript 語(yǔ)法。一個(gè)最簡(jiǎn)單的 pantofile.js 內(nèi)容如下:
module.exports = panto => {};
注意 Panto 對(duì) Node.js 的最低版本要求是 6.0.0,因此可以放心使用 ES2015 的語(yǔ)法。
接著,就像加載 Grunt/Gulp 的插件一樣,需要先加載轉(zhuǎn)換器(Transformer)。轉(zhuǎn)換器定義了如何變換文件內(nèi)容的邏輯。
module.exports = panto => { panto.loadTransformer('read');
panto.loadTransformer('less');
panto.loadTransformer('copy');
panto.loadTransformer('write');
};
以上需要使用npm加載相應(yīng)的package:
npm install panto panto-transformer-read panto-transformer-less panto-transformer-copy panto-transformer-write --save-dev
下面先要定義幾個(gè)參數(shù):cwd、src、output。其中 src、output 相對(duì)于 cwd:
panto.setOptions({
cwd: __dirname,
src: 'src',
output: 'output' });
現(xiàn)在開(kāi)始定義構(gòu)建流程,這里以轉(zhuǎn)換less文件為例:
panto.pick('*.less').read().less().write();
這個(gè)流程的意義是在 src 目錄內(nèi)搜索以 .less 為擴(kuò)展名的文件,并讀取其內(nèi)容,轉(zhuǎn)換為CSS格式,并寫(xiě)入 output 的對(duì)應(yīng)目錄內(nèi)。比如 src/style/foo.less,轉(zhuǎn)換后寫(xiě)入 output/style/foo.less。
現(xiàn)在,我們把除了less文件以外的其它文件拷貝到 output 中:
panto.rest().copy();
那么 src/config/c.yml 拷貝至 output/config/c.yml。
現(xiàn)在,完整的構(gòu)建配置文件的內(nèi)容是:
module.exports = panto => { panto.loadTransformer('read');
panto.loadTransformer('less');
panto.loadTransformer('copy');
panto.loadTransformer('write');
panto.setOptions({
cwd: __dirname,
src: 'src',
output: 'output' }); panto.pick('*.less').read().less().write(); panto.rest().copy();
};
你可以使用 load-panto-transformers 來(lái)避免書(shū)寫(xiě)大量 panto.loadTransformer('xxx') 語(yǔ)句,同時(shí)你也可以使用 time-panto來(lái)監(jiān)控構(gòu)建信息,這樣,簡(jiǎn)化并完整的 pantofile.js 是:
module.exports = panto => { require('load-panto-transformers')(panto);
require('time-panto')(panto);
panto.setOptions({
cwd: __dirname,
src: 'src',
output: 'output' }); panto.pick('*.less').read().less().write(); panto.rest().copy();
};
最后,為了在命令行環(huán)境下調(diào)用構(gòu)建任務(wù),你需要先安裝 panto-cli:
npm install panto-cli -g
在命令行中執(zhí)行:
panto -f pantofile.js
以上示例請(qǐng)參考 https://github.com/pantojs/simple-panto-usage。
轉(zhuǎn)換器
轉(zhuǎn)換器(Transformer) 定義了如何轉(zhuǎn)換文件的邏輯。實(shí)現(xiàn)一個(gè) Transformer 可以繼承自 panto-transformer:
const Transformer = require('panto-transformer');
class FooTransformer extends Transformer {
_transform(file) {
file.content += 'A'; return Promise.resolve(file);
} isTorrential() {
return false;
} isCacheable() {
return true;
}
}
module.exports = FooTransformer;
如果文件的轉(zhuǎn)換是相互獨(dú)立的,那么實(shí)現(xiàn) _transform 方法即可,否則需要實(shí)現(xiàn) transformAll 方法,它們都返回 Promise 對(duì)象,兩種轉(zhuǎn)換器使用 isTorrential() 方法來(lái)區(qū)分。具體請(qǐng)參見(jiàn) panto-transformer-browserify 與 panto-transformer-uglify的不同實(shí)現(xiàn)。
如果轉(zhuǎn)換器是嚴(yán)格冪等的,則是可緩存的,這通過(guò) isCacheable() 方法來(lái)區(qū)分。任何可能通過(guò)文件內(nèi)容之外其它因素導(dǎo)致兩次轉(zhuǎn)換結(jié)果不一致的情景,都不能是可緩存的。例如,計(jì)算內(nèi)容md5值的邏輯,只要內(nèi)容相同,md5值即是一樣的,不涉及任何其它因素,這就是可緩存的。再例如,為文件增加當(dāng)前時(shí)間的時(shí)間戳內(nèi)容,則一定是不可緩存的。
轉(zhuǎn)換器的輸入和輸出都是文件對(duì)象或者是集合。文件對(duì)象是一個(gè)純JavaScript對(duì)象(PlainObject),至少包含 filename 和 content 兩個(gè)屬性,你也可以增加其它屬性。
流
Panto 使用流(Stream)來(lái)定義轉(zhuǎn)換任務(wù)。作為節(jié)點(diǎn),流可以構(gòu)建為任意的有向無(wú)環(huán)圖。
const Stream = require('panto').Stream;
const s1 = new Stream();
const s2 = new Stream();
const s3 = new Stream();
const s4 = new Stream();
s1.connect(s2).connect(s4);
s1.connect(s3);
以上代碼構(gòu)建的拓?fù)鋱D如下:
流以一個(gè)轉(zhuǎn)換器為構(gòu)造參數(shù),但也可以不傳入任何參數(shù)。
new Stream(new Transformer())
通過(guò)定義拓?fù)淞骱娃D(zhuǎn)換器,可以簡(jiǎn)潔和清晰地描述如何構(gòu)建一個(gè)項(xiàng)目。下面是一個(gè)復(fù)雜的構(gòu)建流程示例:
一個(gè)更典型的配置案例:
module.exports = panto => { panto.setOptions({
cwd: __dirname,
src: 'src',
output: 'output' // 不可以與src相同 });
require('load-panto-transformers')(panto);
const srcJs = panto.pick('**/*.{js,jsx}').tag('js(x)').read();
srcJs.babel(clientBabelOptions).write();
srcJs.babel(serverBabelOptions).write();
panto.pick('**/*.less').tag('less').read().less().write(); // node_modules 的文件只需處理一次
panto.pick('node_modules/**/*', true).tag('node_modules').copy();
panto.rest().tag('others').ignore().copy();
}