Cover image from https://www.grimms.eu/en/products/building-amp-rainbow-worlds/organic-shapes/1240/colored-waldorf-blocks
Today I wrote a tiny babel plugin for reducing the size of a vue proyect by transforming the props
attribute in their minimal expression (and at the same time, removing vue-types
as a dependency).
Steps I did to learn how to write a babel plugin
To understand (at least something, lol) how babel's plugin system works, I did the following:
- Set up vscode to be able to debug the plugin.
- Read the plugin section of the babel-handbook: https://github.com/jamiebuilds/babel-handbook. I didn't read it all, though. And the first time didn't understood anything.
- Read other plugins' code.
- Install
@types/babel__core
. The autocomplete (even in JS) in vscode is lit. Really helpful! - Debug a lot. In conjunction with the handbook, it made me understand a little how to understand how the code is interpreted and how to modify it.
- Add jsdoc everytime you can. You help vscode to help you ;)
The following snipet is the vscode launch's configuration for debugging a babel plugin:
{
"type": "node",
"request": "launch",
"name": "Debug babel",
"console": "integratedTerminal",
"autoAttachChildProcesses": true,
"program": "${workspaceFolder}/node_modules/@babel/cli/bin/babel.js",
"args": [
"--config-file=${workspaceFolder}/babel.config.js",
"${workspaceFolder}/path/to/file.js"
]
}
The string "${workspaceFolder}/path/to/file.js"
is the file to be compiled.
Babel plugin basic structure
const { declare } = require('@babel/helper-plugin-utils');
const { types: t } = require('@babel/core');
module.exports = declare(api => {
// If the plugin requires babel 7++
api.assertVersion(7);
return {
// For what I learned, although the name is not required,
// if you add one, remember to NOT add the "babel-plugin"
// prefix. E.g., if the package's name is
// "babel-plugin-transform-vue-props", the name would be
// the following:
name: 'transform-vue-props',
visitor: {
/**
* @param {babel.types.ImportDeclaration} path Import's node
* @return {void}
*/
ImportDeclaration(path) {
if (path.node.source.value === 'vue-types') {
path.remove();
}
},
},
};
});
The visitors
prop its where everything happens.
When we talk about "going" to a node, we actually mean we are visiting them.
Each node has a type, and everyone of them can be visited. In the example above we are visiting each import declaration and remove them if they are importing the vue-types
library.
How to transform code
By the way, if you want to transform, e.g. an object, into an array of strings (the keys), you would have to do the following:
Consider this code:
const obj = {
name: 'Luciano',
age: 28,
};
If you want to transform it to this:
const obj = ['name', 'age'];
You would have to do the following:
const { declare } = require('@babel/helper-plugin-utils');
const { types: t } = require('@babel/core');
module.exports = declare(() => {
return {
name: 'transform-obj-to-array',
visitor: {
/**
* @param {babel.types.VariableDeclarator} path Declaration
* @return {void}
*/
VariableDeclarator(path) {
const node = path.node;
if (!t.isObjectExpression(node.init)) {
return;
}
node.init = t.arrayExpression(
node.init.properties.map(prop => t.stringLiteral(prop.key.name)),
);
},
},
};
});
By the way, I had to debug it until I could find the correct visitor (wasn't
VariableDeclaration
norAssignmentExpression
). JSDoc +@types/babel__core
+ VSCode FTW.
As you can see, isn't as simple as to replace it like a string. The types
(aka t
) prop from @babel/core
it's very helpful for validating what structure is something and for building new ones.
babel-plugin-transform-vue-props
- The lib can be found here https://github.com/lgraziani2712/babel-plugin-transform-vue-props
- It really helps to remove
vue-types
as dependency (since it does nothing in production), which can be weight between 25kb~ to 3.5kb~ gzipped and if webpack (or any bundler) is configured to use the production file (https://github.com/dwightjack/vue-types#production-build). This size doesn't counts the use ofvue-types
in every component.
Motivation
I reaaaally love to resolve optimization problems, and I wanted to remove vue-types
from the production bundle. I searched everywhere but didn't find anything confortable to use. I also rediscovered this doc https://vuejs.org/v2/guide/components-props.html#Prop-Types and remember what is the most simple definition of the props
attribute of a component.
EDIT: I just found https://astexplorer.net/. Is really dope!
I hope this post will motivate anyone who wants to explore the world of babel plugins but don't know where or how to start! Cheers!
I wrote my first babel plugin here github.com/sanketmaru/babel-plugin....
Couple of issues i am unable to proceed with and wanted help
I am trying to remove the functions based upon some param and it is not able to hit ClassMethod visitor in production mode, where as in development mode it works.
While debugging in vs code. Sometimes the breakpoints hit and sometimes it doesn't. Have you faced this issue ?
The development/production mode can be altered from build.js ( its a create-react-app).