See http://www.infoq.com/cn/articles/constructe-tool-optimize-for-complex-web-front-end-projects.
PantoJS is an extremely flexible file transforming engine. It is usually used for building projects, especially web front-end projects.
It works like Grunt or Gulp, but more efficient, powerful and flexible.
- Any building process topology
Panto supports almost any building process
- Collecting rest files
You can select rest files that not selected by selectors
- Read a file once at most
Read a file only once, even more than one transforming on a file
- Transform a file once at most
Avoid duplicated processing
- Cache at file
Avoid duplicated transforming
- Incremental file transforming
More efficient watching
Grunt | Gulp | Panto | |
---|---|---|---|
Stream task | ✘ | ✔ | ✔ |
Topological process | ✘ | ✘ | ✔ |
Read once | ✘ | ✘ | ✔ |
Accurate cache | ✘ | ✘ | ✔ |
Accurate increment | ✘ | ✘ | ✔ |
Like Grunt or Gulp, Panto needs a process configurable file pantofile.js in root directory of your project, coffeescript syntax is not supported. A simplest pantofile.js contains:
module.exports = panto => {};
Notice that Panto requires Node.js 6.0.0 or higher, so feel relieved to write ES2015 codes.
Like loading Grunt/Gulp plugins, transformers should be loaded first. A transformer defines how to transform file contents.
module.exports = panto => {
panto.loadTransformer('read');
panto.loadTransformer('less');
panto.loadTransformer('copy');
panto.loadTransformer('write');
};
The above needs to install some npm packages:
npm install panto panto-transformer-read panto-transformer-less panto-transformer-copy panto-transformer-write --save-dev
Next, we need to define some parameters: cwd
/src
/output
. src
and output
are relative to cwd
:
panto.setOptions({
cwd: __dirname,
src: 'src',
output: 'output'
});
Now we start to define building process. Here we transform .less files as an example:
panto.pick('*.less').read().less().write();
The above codes search .less files in src
directory, read them, transform to css format, and write to output
. E.g., src/style/foo.less transformed to output/style/foo.less.
Then we copy files other than .less files to output
:
panto.rest().copy();
i.e. src/config/c.yml copied to output/config/c.yml.
The total pantofile.js is:
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();
};
You can use load-panto-transformers to avoid writing many panto.loadTransformer('xxx') statements. time-panto is used for monitor, the simpler pantofile.js is:
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();
};
At last, for starting tasks in terminal, you need to install panto-cli first:
npm install panto-cli -g
Run:
panto -f pantofile.js
All above are in https://github.com/pantojs/simple-panto-usage.
Transformers define logic of how to transform files. Extend panto-transformer to implement a 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;
If the files are transformed independently, just implement _transform() function, or else transformAll(), they both return Promise
object, distinguished by isTorrential() function. Please see panto-transformer-browserify and panto-transformer-uglify.
If a transformer is idempotent strictly, it's cacheable, isCacheable() returns true. Any factors beyond file content that affect transforming results between two transformings will lead to uncacheable. E.g., for calculating md5 of content, same content result in same md5 value, affected by no factors. As another example, appending current date time to file content result in uncacheable, of course.
Input and output of transformers are files or file arrays. A file is a plain JavaScript object, contains filename and content these two properties at least. You can append other properties too.
Panto uses stream to define transforming tasks. As a node, streams consist of a directed acyclic graph.
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);
Above codes construct a topology graph:
A stream needs a transformer as a constructing parameter, or nothing is acceptable too.
new Stream(new Transformer())
By defining topological streams and transformers, you can describe how to build a project easily and clearly. The following is a complicated building process topology:
A more typical configurable case:
module.exports = panto => {
panto.setOptions({
cwd: __dirname,
src: 'src',
output: 'output' // not as same as 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 should be processed only once
panto.pick('node_modules/**/*', true).tag('node_modules').copy();
panto.rest().tag('others').ignore().copy();
}
Panto
is available through API:
const panto = require('panto');
panto.setOptions({
});
panto.on('start', buildId => {})
.on('flowstart', ({tag}, flowId) => {})
.on('flowend', ({tag}, flowId) => {})
.on('error', (err, buildId) => {})
.on('complete', (files, buildId) => {})
panto.build().then(() => {
panto.watch();
});
- cwd: string, current working directory, default
process.cwd()
- src: string, source directory, default '.'
- output: output string, file directory, default 'output'
- binary_resource: string, binary file extensions, e.g. 'zip,tar,jpg', default is same as binary-extensions
- watch_ignore: array, ignored files when watching, e.g. '["/*.pyc", "/*.class"]', default is []
Some official transformers: