Babel is a JS-to-JS translator that supports es Next, typescript, flow, and other grammars via syntax plug-ins, supports AST conversion plug-ins, and finally generates object code and Sourcemap.

Postcss is a CSS-to-CSS translator that supports syntax such as less, Sass, stylus via syntax plug-ins, as well as AST conversion plug-ins, and finally generates object code and sourcemap.

Postcss is to CSS what Babel is to JS, postCSS has a thriving system of plug-ins such as Autoprefixer and stylelint.

Both tools are similar in orientation and are used for code translation and static analysis. But there are differences in API design.

By the end of this article you will know:

  • Babel, PostCSS API easy to use
  • Babel, postCSS compilation process
  • Postcss source code architecture
  • Postcss and Babel differ in API design
  • What are the benefits of chained and centralized API styles

babel api

Let’s start with the familiar Babel API:

const babel = require('@babel/core');

const code = ` console.log('hello world'); `;
const { code, map } = babel.transformSync(code, {
    plugins: [
        [
            pluginA,
            {
                // options}]],sourceMaps: true
});
Copy the code

Uses the Babel Core package’s transformSync API, passed in source code and conversion plug-ins. Babel will internally parse source code to AST, transform AST, and generate object code and Sourcemap.

We can use the source code of babel3 to see what is done internally (the source code of babel3 is clear, if you are interested, you can go to see it) :

Babel first stores information such as the source code of the File being processed through the File object, and then calls Parse

Parse, transform, generate The parse method completes the three steps:

Parse used Acorn directly with Babel 3 (Babel 7’s Parser was forked by Acorn) :

The Transform phase calls all the built-in Transformer plug-ins to transform the AST:

The Generate phase generates object code and sourcemap:

Babel has done many iterations, such as splitting into packages, but the process remains the same.

We sometimes use lower-level packages instead of using Babel Core:

const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const generate = require('@babel/generator').default;

const code = ` console.log('hello world'); `;

const ast = parser.parse(code);

traverse(ast, {
    visitor: {
        CallExpression(path) {
            //xxx}}});const { code, map } = generate(ast, {
    sourceMaps: true
});
Copy the code

In fact, @babel/core is based on @babel/ Parser, @babel/traverse, @babel/ Generator and other packages to do the packaging, support plug-in capabilities. If you don’t need plug-ins, just use these packages.

postcss api

The POSTCSS API looks like this:

const postcss = require('postcss');
const autoprefixer = require('autoprefixer')

const css = ` @import "aaa"; a { background: color; } `;

postcss([autoprefixer]).process(css).then(result= > {
 console.log(result.css)
})  
Copy the code

The postCSS function is called to pass in the array of plug-ins, the process method is called to process the CSS passed in, and then is called to get the result in the callback (object code and sourcemap).

Why is this order? Let’s take a look at it from the source:

The postCSS method called first returns a Processor object. The Processor object has methods such as use (applying plug-ins) and Process (processing CSS), so it is Postcss ([]).process

Then we can see that Process returns a LazyResult object. The LazyResult object calls parser to parse the incoming CSS and provides the then method to return the result. So postcss([]).process(CSS).then(() => {}).

Then calls ayNC, applies the plugin, processes the AST (root), and returns the result of stringifier.

The Stringifier calls MapGenerator to print the AST as the target CSS and generate sourcemap.

This is how postCSS is compiled.

Postcss is still a parse, transform, generate step, but with a different API.

Postcss has these classes inside:

  • ProcessorLazyResult is returned when the plugin is passed in. There are use (passed in) and process (returned) methods
  • LazyResultParser is called to parse the source code. Then (get results asynchronously) the AST is converted using the plug-in and MapGenerator is called to print the AST
  • Parser: Pass in the source code and return the AST
  • MapGenerator: Pass in the AST, print it as object code, and generate sourcemap.

So back to the beginning, the way we call it is

postcss([autoprefixer]).process(css).then(result= > {
 console.log(result.css)
}) 
Copy the code

Postcss is essentially a factory function that creates the Processsor object. The Processor’s Process method returns the LazyResult object, and the LazyResult object has the then method to return the result.

Chain and centralized API styles

Postcss and Babel are both translations of code (JS, CSS), but their apis are quite different.

Babel is:

const { code, map } = babel.transformSync(code, {
    plugins: [
        [
            pluginA,
            {
                // options}]],sourceMaps: true
});
Copy the code

Postcss is:

postcss([autoprefixer]).process(css).then(result= > {
 console.log(result.css)
}) 
Copy the code

As you can see, Babel has only one API, passing in source code and plug-ins at once, internally completing three phases: Parse, Transform, and generate.

Postcss provides a chained API that passes in plug-ins and source code in multiple steps.

The advantage of postCSS’s chained style is that the code is compact and what is done at each step is clear. The advantage of Babel as a one-time pass is that it is simple to use, but options are coupled together and have high complexity.

For example, jquery, Chalk, Jest and Webpack-chain are all chained, while Webpack, Babel and so on are all passed in at one time.

Jest API Style (Chain) :

test(`jest`.() = > {
    expect(a + b).not.toBeLessThan(expected);
}
Copy the code

Chalk API style (Chain) :

chalk.rgb(123.45.67).underline('Underlined reddish color')
Copy the code

Webpack-chain API Style (Chain) :

config
  .entry('index')
    .add('src/index.js')
    .end()
  .output
    .path('dist')
    .filename('[name].bundle.js');
Copy the code

Webpack API Style (Centralized) :

webpack({
  entry: './src/index.js'.output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',}})Copy the code

conclusion

In this article we explore the internal implementation of Babel and PostCSS API usage, and understand why PostCSS is the call chain of postCSS ([plugins]).process(CSS).then.

After that, the advantages and disadvantages of Babel’s centralized style API and PostCSS’s chain style API are sorted out, and the API styles of other libraries are extended. Knowing that you can design different apis to do the same thing.

Hopefully this article has given you a good idea of what the internal flow of PostCSS and Babel is, why postCSS API call chains are the way they are, and what API styles they are. What are the advantages and disadvantages of chain versus centralized?