@lihautan/babel-plugin-transform-destructure-number
v0.0.7
Published
## Inspiration
Downloads
10
Readme
transform-destructure-number
Inspiration
💡 The Idea
Destructuring decimal number via .
?
Cool! 😎
Too bad it's not a valid syntax:
let a.b = 3.142;
a === 3; // true
b === 142; // true
Which means, this require us to fork the babel parser.
We can create a parser plugin to extend the existing parsing method. This allow us to turn on / off the parser plugin from the transform plugin. This is just like how babel typescript plugin or flow plugin works.
// filename: https://github.com/babel/babel/tree/master/packages/babel-parser/src/plugin-utils.js
import estree from "./plugins/estree";
+import jsWeDoNotDeserve from "./plugins/jsWeDoNotDeserve";
import flow from "./plugins/flow";
import jsx from "./plugins/jsx";
import typescript from "./plugins/typescript";
// ...
export const mixinPlugins: { [name: string]: MixinPlugin } = {
estree,
+ jsWeDoNotDeserve,
jsx,
// ...
We can first copy the original implementation of parseVarId
, the method to parse variable name in a VariableDeclarator
.
If we encounter a .
token after the variable name, we create a different AST node, DestructureNumberPattern
instead.
const a.b = 1.3;
// ...
{
type: "VariableDeclarator",
id: {
type: "DestructureNumberPattern",
left: {
type: "Identifier",
name: "a",
},
right: {
type: "Identifier",
name: "b",
},
},
init: {
type: "NumericLiteral",
value: 1.3,
},
}
// @flow
import { types as tt, type TokenType } from '../tokenizer/types';
import type Parser from '../parser';
import * as N from '../types';
import {
type BindingTypes,
BIND_NONE,
BIND_LEXICAL,
BIND_VAR,
} from '../util/scopeflags';
export default (superClass: Class<Parser>): Class<Parser> =>
class extends superClass {
parseVarId(
decl: N.VariableDeclarator,
kind: 'var' | 'let' | 'const'
): void {
const startPos = this.state.start;
const startLoc = this.state.startLoc;
+ decl.id = this.parseBindingAtom();
+ if (this.eat(tt.dot) && decl.id.type === 'Identifier') {
+ const node = this.startNodeAt(startPos, startLoc);
+ node.left = decl.id;
+ node.right = this.parseIdentifier();
+ decl.id = this.finishNode(node, 'DestructureNumberPattern');
+ }
this.checkLVal(
decl.id,
kind === 'var' ? BIND_VAR : BIND_LEXICAL,
undefined,
'variable declaration',
kind !== 'var'
);
}
checkLVal(
expr: N.Expression,
bindingType: BindingTypes = BIND_NONE,
checkClashes: ?{ [key: string]: boolean },
contextDescription: string,
disallowLetBinding?: boolean
): void {
switch (expr.type) {
case 'DestructureNumberPattern':
break;
default:
super.checkLVal(
expr,
bindingType,
checkClashes,
contextDescription,
disallowLetBinding
);
}
}
};
📘 The Code
export default function ({ types: t, template }) {
return {
name: 'transform-destructure-number',
visitor: {
VariableDeclarator(path) {
if (path.node.id.type === 'DestructureNumberPattern') {
if (t.isNumericLiteral(path.node.init)) {
const [a, b] = path.node.init.extra.raw.split('.');
path.replaceWith(
template.statement`let [${path.node.id.left}, ${
path.node.id.right
}] = [${a}, ${b || '0'}]`().declarations[0]
);
} else {
throw new Error('Destructure number with number!');
}
}
},
},
};
}
🧪 Try it out
<a href="https://twitter.com/share?ref_src=twsrc%5Etfw" class="twitter-share-button" data-show-count="false"
Tweet