Skip to content

Commit

Permalink
implement transform
Browse files Browse the repository at this point in the history
  • Loading branch information
JLHwung committed Mar 2, 2022
1 parent eb02153 commit ed3a81d
Show file tree
Hide file tree
Showing 194 changed files with 3,193 additions and 0 deletions.
1 change: 1 addition & 0 deletions Gulpfile.mjs
Expand Up @@ -462,6 +462,7 @@ function copyDts(packages) {

const libBundles = [
"packages/babel-parser",
"packages/babel-plugin-proposal-destructuring-private",
"packages/babel-plugin-proposal-object-rest-spread",
"packages/babel-plugin-proposal-optional-chaining",
"packages/babel-preset-react",
Expand Down
@@ -0,0 +1,3 @@
src
test
*.log
19 changes: 19 additions & 0 deletions packages/babel-plugin-proposal-destructuring-private/README.md
@@ -0,0 +1,19 @@
# @babel/plugin-proposal-destructuring-private

> Transform destructuring private proposal
See our website [@babel/plugin-proposal-destructuring-private](https://babeljs.io/docs/en/babel-plugin-proposal-destructuring-private) for more information.

## Install

Using npm:

```sh
npm install --save-dev @babel/plugin-proposal-destructuring-private
```

or using yarn:

```sh
yarn add @babel/plugin-proposal-destructuring-private --dev
```
50 changes: 50 additions & 0 deletions packages/babel-plugin-proposal-destructuring-private/package.json
@@ -0,0 +1,50 @@
{
"name": "@babel/plugin-proposal-destructuring-private",
"version": "0.0.0",
"description": "Transform destructuring private proposal",
"repository": {
"type": "git",
"url": "https://github.com/babel/babel.git",
"directory": "packages/babel-plugin-proposal-destructuring-private"
},
"license": "MIT",
"publishConfig": {
"access": "public"
},
"main": "./lib/index.js",
"keywords": [
"babel-plugin"
],
"dependencies": {
"@babel/helper-plugin-utils": "workspace:^",
"@babel/plugin-syntax-destructuring-private": "workspace:^",
"@babel/plugin-transform-destructuring": "workspace:^",
"@babel/plugin-transform-parameters": "workspace:^"
},
"peerDependencies": {
"@babel/core": "^7.17.0"
},
"devDependencies": {
"@babel/core": "workspace:^",
"@babel/helper-plugin-test-runner": "workspace:^",
"@babel/traverse": "workspace:^",
"@babel/types": "workspace:^"
},
"homepage": "https://babel.dev/docs/en/next/babel-plugin-proposal-destructuring-private",
"engines": {
"node": ">=6.9.0"
},
"author": "The Babel Team (https://babel.dev/team)",
"conditions": {
"BABEL_8_BREAKING": [
null,
{
"exports": null
}
]
},
"exports": {
".": "./lib/index.js",
"./package.json": "./package.json"
}
}
205 changes: 205 additions & 0 deletions packages/babel-plugin-proposal-destructuring-private/src/index.ts
@@ -0,0 +1,205 @@
import { declare } from "@babel/helper-plugin-utils";
import syntaxDestructuringPrivate from "@babel/plugin-syntax-destructuring-private";
import {
hasPrivateKeys,
hasPrivateClassElement,
transformPrivateKeyDestructuring,
buildVariableDeclarationFromParams,
} from "./util";
import { convertFunctionParams } from "@babel/plugin-transform-parameters";

import type { PluginPass } from "@babel/core";
import type { Visitor } from "@babel/traverse";
import { types as t } from "@babel/core";

const {
assignmentExpression,
cloneNode,
expressionStatement,
isExpressionStatement,
isIdentifier,
isSequenceExpression,
sequenceExpression,
variableDeclaration,
variableDeclarator,
} = t;

export default declare(function ({
assertVersion,
assumption,
}: {
assumption: (string) => boolean;
assertVersion: (string) => void;
}) {
assertVersion("^7.17.0");

const ignoreFunctionLength = assumption("ignoreFunctionLength");
const objectRestNoSymbols = assumption("objectRestNoSymbols");

const privateKeyDestructuringVisitor: Visitor<PluginPass> = {
Function(path) {
// (b, { #x: x } = I) => body
// transforms to:
// (p1, p2) => { var b = p1, { #x: x } = p2 === undefined ? I : p2; body; }
if (!path.node.params.some(param => hasPrivateKeys(param))) return;
// wrap function body within IIFE if any param is shadowed
convertFunctionParams(path, ignoreFunctionLength, () => false, false);
const { node, scope } = path;
const { params, variableDeclaration } =
buildVariableDeclarationFromParams(node.params, scope);

path
.get("body") // invariant: path.body is always a BlockStatement
.unshiftContainer("body", variableDeclaration);
node.params = params;
scope.crawl();
// the pattern will be handled by VariableDeclaration visitor.
},
CatchClause(path) {
// catch({ #x: x }) { body }
// transforms to:
// catch(_e) { var {#x: x } = _e; body }
const { node, scope } = path;
if (!hasPrivateKeys(node.param)) return;
// todo: handle shadowed param as we did in convertFunctionParams
const ref = scope.generateUidIdentifier("e");
path
.get("body")
.unshiftContainer(
"body",
variableDeclaration("var", [variableDeclarator(node.param, ref)]),
);
node.param = cloneNode(ref);
// the pattern will be handled by VariableDeclaration visitor.
},
ForXStatement(path) {
const { node, scope } = path;
const leftPath = path.get("left");
if (leftPath.isVariableDeclaration()) {
const left = leftPath.node;
if (!hasPrivateKeys(left.declarations[0].id)) return;
// for (const { #x: x } of cls) body;
// transforms to:
// for (const ref of cls) { const { #x: x } = ref; body; }
const declarator = scope.generateUidIdentifier("ref");
node.left = variableDeclaration(left.kind, [
variableDeclarator(declarator, null),
]);
left.declarations[0].init = cloneNode(declarator);
path.ensureBlock();
// todo: handle shadowed variables referenced in computed keys:
// var a = 0;for (const { #x: x, [a++]: y } of z) { const a = 1; }
const blockBody = (node.body as t.BlockStatement).body;
blockBody.unshift(left);
scope.crawl();
// the pattern will be handled by VariableDeclaration visitor.
} else if (leftPath.isPattern()) {
if (!hasPrivateKeys(leftPath.node)) return;
// for ({ #x: x } of cls);
// transforms to:
// for (const ref of cls) { ({ #x: x } = ref); body; }
// This transform assumes that any expression within the pattern
// does not interfere with the iterable `cls`.
const declarator = scope.generateUidIdentifier("ref");
node.left = variableDeclaration("const", [
variableDeclarator(declarator, null),
]);
path.ensureBlock();

const blockBody = (node.body as t.BlockStatement).body;

// preserve completion record:
// for ({ #x: x } of []);
if (blockBody.length === 0 && path.isCompletionRecord()) {
blockBody.unshift(expressionStatement(scope.buildUndefinedNode()));
}

// todo: handle shadowed variables referenced in computed keys:
// var a = 0, x;for ({ #x: x, [a++]: y } of z) { const a = 1; }
blockBody.unshift(
expressionStatement(
assignmentExpression("=", leftPath.node, cloneNode(declarator)),
),
);
}
},
VariableDeclaration(path, state) {
const { scope, node } = path;
const { declarations } = node;
if (!declarations.some(declarator => hasPrivateKeys(declarator.id))) {
return;
}
const newDeclarations = [];
for (const declarator of declarations) {
for (const { left, right } of transformPrivateKeyDestructuring(
declarator.id,
declarator.init,
scope,
/* isAssignment */ false,
name => state.addHelper(name),
objectRestNoSymbols,
/* useBuiltIns */ true,
)) {
newDeclarations.push(variableDeclarator(left, right));
}
}
node.declarations = newDeclarations;
scope.crawl();
},

AssignmentExpression(path, state) {
const { node, scope, parent } = path;
if (!hasPrivateKeys(node.left)) return;
const assignments = [];
for (const { left, right } of transformPrivateKeyDestructuring(
node.left,
node.right,
scope,
/* isAssignment */ true,
name => state.addHelper(name),
objectRestNoSymbols,
/* useBuiltIns */ true,
)) {
assignments.push(assignmentExpression("=", left, right));
}
// preserve completion record
if (
(!isExpressionStatement(parent) && !isSequenceExpression(parent)) ||
path.isCompletionRecord()
) {
const { left } = assignments[0];
if (scope.isStatic(node.right)) {
const tempId = scope.generateDeclaredUidIdentifier("m");
assignments.unshift(
assignmentExpression("=", tempId, cloneNode(node.right)),
);
assignments.push(cloneNode(tempId));
} else if (
!isIdentifier(assignments[assignments.length - 1].right, {
name: left.name,
})
) {
// If node.right is non-static and then the left is an effectively-constant memoised id
// If the last assignment does not end with left, that we can safely reuse `left` as the completion value
assignments.push(cloneNode(left));
}
}

path.replaceWith(sequenceExpression(assignments));
scope.crawl();
},
};

const visitor: Visitor<PluginPass> = {
Class(path, state) {
if (!hasPrivateClassElement(path.node.body)) return;
path.traverse(privateKeyDestructuringVisitor, state);
},
};

return {
name: "proposal-destructuring-private",
inherits: syntaxDestructuringPrivate,
visitor: visitor,
};
});

0 comments on commit ed3a81d

Please sign in to comment.