My FeedDiscussionsHeadless CMS
New
Sign in
Log inSign up
Learn more about Hashnode Headless CMSHashnode Headless CMS
Collaborate seamlessly with Hashnode Headless CMS for Enterprise.
Upgrade ✨Learn more

Discarding Gulp.js — Why I created Panto.js

Yanni Nightingale's photo
Yanni Nightingale
·Sep 6, 2016

I need almighty with excellent performance!

You cannot imagine how complicated your project will transform into, in the near future.

Three years ago, like many other front end developers, I was writing JavaScript (ES3 syntax) and CSS everyday. But things changed so quickly, nowadays, we write ES2017, JSX, TypeScript, and Less or Sass more than regular JavaScript, and CSS, and with more and more incoming preprocess languages!

Isomorphic JavaScript needs a JS file to be compiled both on the server and the client.

As more and more modular systems show up; the dependency relationship is getting complex.

And more ...

The building process has never been wilder as it is now!

So, which building tool should I pick up?

Grunt? No, it's popular with a complete ecosystem, but it requires a dense IO on disk, which slows down the progress.

Gulp? Good, that sounds proper. But I have some special requirements:

  1. Read a file only once at most to speed up the building process
  2. A file goes through a transformer only once at most
  3. Incremental transforming by file, instead of task
  4. Pick the files that are not selected

How can I do this?

I built Panto.js

Panto.js is an extremely flexible engine for transforming files. So what is transforming? Uglify-ing is, babelify-ing is, browserify-ing is, and anything that could modify file's content is transforming too. That's very extensive.

Building a project, is transforming files.

Using Gulp.js to compile a ES2015 javascript:

gulp.src('entry.js').pipe(babel()).pipe(uglify()).pipe(gulp.write());

Using Panto.js:

panto.$('entry.js').babel().uglify().write();

Improvement

1. Share Reading

Problems come over. I have a bulk of javascript files written in ES2017, some of them need to be transformed to ES2015 to work in Node.js, some need to be transformed to ES5 to work in browsers, and others both.

Using Gulp.js:

gulp.src(['server/*.js', 'common/*.js']).pipe(babel(es2015))
gulp.src(['client/*.js', 'common/*.js']).pipe(babel(es5))

Obviously, common/*.js will be read twice, that's not what I wanna see. Panto.js fixes that:

panto.$(['server/*.js', 'common/*.js']).babel(es2015)
panto.$(['client/*.js', 'common/*.js']).babel(es5)

Panto.js makes sure that common/*.js will be read only ONCE by sharing the same file object between two tasks.

2. Collection Merging

In this case, I need to concat some css files, but, some of them come from less, others come from sass.

Using Gulp.js:

Require disk IO? I don't know how to implement it. Could anyone add a comment? Thanks!

Using Panto.js:

const concatStream = new Panto.Stream(new ConcatTransformer());

panto.$('*.less').connect(concatStream);
panto.$('*.sass').connect(concatStream);

The code block above describes a topology graph:

title here

3. Incremental transforming

It costs a lot to build the whole project, so developers always startup a watch:

gulp.watch('*.js', funtion() {
    gulp.src('*.js')....
});

That's ugly, and you have to write many watch statements. What's more, the files selected will be read and transformed all over.

For example, when entry.js is changed, all the other javascript files will be immediately read and transformed, even if it's not necessary. And another file that might not match *.js, but does depend on entry.js, should be re-transformed too, right?

Panto.js fixes these issues by maintaining a dependency graph. It looks like this:

Dependency Graph

When a.png is modified, a.js, a.css, b.css all will be re-transformed again. That's the exact behavior.

Panto.js has an API called reportDependencies() to define this graph.

4. Rest(Left/Not Selected) file collection

Grunt.js and Gulp.js both need to match some files to define tasks, so does Panto.js.

But, what if I want to get the unmatched files?

Panto.js has an API called rest() to match these files:

panto.rest().copy()

Conclusion

Gulp.js is great, but I need something more efficient, powerful and flexible. That's why I have created Panto.js. It can be setup as easily as Gulp.js. And extending it is much easier, search for panto+transformer on npm.

Panto.js is hosted on https://github.com/pantojs/panto. If you're interested in it, please star.

https://github.com/pantojs/panto-best-practice is a great boilerplate showing how to use panto.