Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix handling of this&co in computed keys in arrows transform #14005

Merged
merged 5 commits into from Nov 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -19,6 +19,7 @@
],
"dependencies": {
"@babel/helper-annotate-as-pure": "workspace:^",
"@babel/helper-environment-visitor": "workspace:^",
"@babel/helper-function-name": "workspace:^",
"@babel/helper-member-expression-to-functions": "workspace:^",
"@babel/helper-optimise-call-expression": "workspace:^",
Expand Down
11 changes: 7 additions & 4 deletions packages/babel-helper-create-class-features-plugin/src/fields.ts
@@ -1,9 +1,8 @@
import { template, traverse, types as t } from "@babel/core";
import type { File } from "@babel/core";
import type { NodePath, Visitor, Scope } from "@babel/traverse";
import ReplaceSupers, {
environmentVisitor,
} from "@babel/helper-replace-supers";
import ReplaceSupers from "@babel/helper-replace-supers";
import environmentVisitor from "@babel/helper-environment-visitor";
import memberExpressionToFunctions from "@babel/helper-member-expression-to-functions";
import type {
Handler,
Expand Down Expand Up @@ -851,7 +850,11 @@ function buildPrivateMethodDeclaration(
);
}

const thisContextVisitor = traverse.visitors.merge([
const thisContextVisitor = traverse.visitors.merge<{
classRef: t.Identifier;
needsClassRef: boolean;
innerBinding: t.Identifier;
}>([
{
ThisExpression(path, state) {
state.needsClassRef = true;
Expand Down
@@ -1,9 +1,9 @@
import { template, traverse, types as t } from "@babel/core";
import type { File } from "@babel/core";
import type { NodePath, Scope, Visitor, Binding } from "@babel/traverse";
import { environmentVisitor } from "@babel/helper-replace-supers";
import environmentVisitor from "@babel/helper-environment-visitor";

const findBareSupers = traverse.visitors.merge([
const findBareSupers = traverse.visitors.merge<NodePath<t.CallExpression>[]>([
{
Super(path: NodePath<t.Super>) {
const { node, parentPath } = path;
Expand Down
3 changes: 3 additions & 0 deletions packages/babel-helper-environment-visitor/.npmignore
@@ -0,0 +1,3 @@
src
test
*.log
19 changes: 19 additions & 0 deletions packages/babel-helper-environment-visitor/README.md
@@ -0,0 +1,19 @@
# @babel/helper-environment-visitor

> Helper visitor to only visit nodes in the current 'this' context

See our website [@babel/helper-environment-visitor](https://babeljs.io/docs/en/babel-helper-environment-visitor) for more information.

## Install

Using npm:

```sh
npm install --save-dev @babel/helper-environment-visitor
```

or using yarn:

```sh
yarn add @babel/helper-environment-visitor --dev
```
29 changes: 29 additions & 0 deletions packages/babel-helper-environment-visitor/package.json
@@ -0,0 +1,29 @@
{
"name": "@babel/helper-environment-visitor",
"version": "7.16.0",
"description": "Helper visitor to only visit nodes in the current 'this' context",
"repository": {
"type": "git",
"url": "https://github.com/babel/babel.git",
"directory": "packages/babel-helper-environment-visitor"
},
"homepage": "https://babel.dev/docs/en/next/babel-helper-environment-visitor",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"main": "./lib/index.js",
nicolo-ribaudo marked this conversation as resolved.
Show resolved Hide resolved
"exports": {
".": "./lib/index.js"
},
"dependencies": {
"@babel/types": "workspace:^"
},
"devDependencies": {
"@babel/traverse": "workspace:^"
},
"engines": {
"node": ">=6.9.0"
},
"author": "The Babel Team (https://babel.dev/team)"
}
38 changes: 38 additions & 0 deletions packages/babel-helper-environment-visitor/src/index.ts
@@ -0,0 +1,38 @@
import type { NodePath } from "@babel/traverse";
import { VISITOR_KEYS, staticBlock } from "@babel/types";
import type * as t from "@babel/types";

// TODO (Babel 8): Don't export this function.
export function skipAllButComputedKey(
path: NodePath<t.Method | t.ClassProperty>,
) {
// If the path isn't computed, just skip everything.
if (!path.node.computed) {
path.skip();
return;
}

// So it's got a computed key. Make sure to skip every other key the
// traversal would visit.
const keys = VISITOR_KEYS[path.type];
for (const key of keys) {
if (key !== "key") path.skipKey(key);
}
}

// Methods are handled by the Method visitor; arrows are not skipped because they inherit the context.
const skipKey = process.env.BABEL_8_BREAKING
? "StaticBlock|ClassPrivateProperty|TypeAnnotation|FunctionDeclaration|FunctionExpression"
: (staticBlock ? "StaticBlock|" : "") +
"ClassPrivateProperty|TypeAnnotation|FunctionDeclaration|FunctionExpression";

// environmentVisitor should be used when traversing the whole class and not for specific class elements/methods.
// For perf reasons, the environmentVisitor might be traversed with `{ noScope: true }`, which means `path.scope` is undefined.
// Avoid using `path.scope` here
export default {
[skipKey]: path => path.skip(),

"Method|ClassProperty"(path: NodePath<t.Method | t.ClassProperty>) {
skipAllButComputedKey(path);
},
};
Expand Up @@ -538,7 +538,7 @@ export interface HandlerState<State = {}> extends Handler<State> {
handle(
this: HandlerState<State> & State,
member: Member,
noDocumentAll: boolean,
noDocumentAll?: boolean,
): void;
memoiser: AssignmentMemoiser;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-helper-module-transforms/package.json
Expand Up @@ -15,8 +15,8 @@
},
"main": "./lib/index.js",
"dependencies": {
"@babel/helper-environment-visitor": "workspace:^",
"@babel/helper-module-imports": "workspace:^",
"@babel/helper-replace-supers": "workspace:^",
"@babel/helper-simple-access": "workspace:^",
"@babel/helper-split-export-declaration": "workspace:^",
"@babel/helper-validator-identifier": "workspace:^",
Expand Down
@@ -1,4 +1,4 @@
import { environmentVisitor } from "@babel/helper-replace-supers";
import environmentVisitor from "@babel/helper-environment-visitor";
import traverse from "@babel/traverse";
import { numericLiteral, unaryExpression } from "@babel/types";
import type * as t from "@babel/types";
Expand Down
1 change: 1 addition & 0 deletions packages/babel-helper-replace-supers/package.json
Expand Up @@ -14,6 +14,7 @@
},
"main": "./lib/index.js",
"dependencies": {
"@babel/helper-environment-visitor": "workspace:^",
"@babel/helper-member-expression-to-functions": "workspace:^",
"@babel/helper-optimise-call-expression": "workspace:^",
"@babel/traverse": "workspace:^",
Expand Down
56 changes: 11 additions & 45 deletions packages/babel-helper-replace-supers/src/index.ts
@@ -1,22 +1,28 @@
import type { HubInterface, NodePath, Scope } from "@babel/traverse";
import traverse from "@babel/traverse";
import memberExpressionToFunctions from "@babel/helper-member-expression-to-functions";
import type { HandlerState } from "@babel/helper-member-expression-to-functions";
import optimiseCall from "@babel/helper-optimise-call-expression";
import environmentVisitor from "@babel/helper-environment-visitor";
import {
VISITOR_KEYS,
assignmentExpression,
booleanLiteral,
callExpression,
cloneNode,
identifier,
memberExpression,
sequenceExpression,
staticBlock,
stringLiteral,
thisExpression,
} from "@babel/types";
import type * as t from "@babel/types";

// TODO (Babel 8): Don't export this.
export {
default as environmentVisitor,
skipAllButComputedKey,
} from "@babel/helper-environment-visitor";

/**
* Creates an expression which result is the proto of objectRef.
*
Expand All @@ -38,49 +44,9 @@ function getPrototypeOfExpression(objectRef, isStatic, file, isPrivateMethod) {
return callExpression(file.addHelper("getPrototypeOf"), [targetRef]);
}

export function skipAllButComputedKey(
path: NodePath<t.Method | t.ClassProperty | t.ClassPrivateProperty>,
) {
// If the path isn't computed, just skip everything.
// @ts-expect-error todo(flow->ts) check node type before cheking the property
if (!path.node.computed) {
path.skip();
return;
}

// So it's got a computed key. Make sure to skip every other key the
// traversal would visit.
const keys = VISITOR_KEYS[path.type];
for (const key of keys) {
if (key !== "key") path.skipKey(key);
}
}

// environmentVisitor should be used when traversing the whole class and not for specific class elements/methods.
// For perf reasons, the environmentVisitor will be traversed with `{ noScope: true }`, which means `path.scope` is undefined.
// Avoid using `path.scope` here
export const environmentVisitor = {
// todo (Babel 8): remove StaticBlock brand checks
[`${staticBlock ? "StaticBlock|" : ""}ClassPrivateProperty|TypeAnnotation`](
path: NodePath,
) {
path.skip();
},

Function(path: NodePath) {
// Methods will be handled by the Method visit
if (path.isMethod()) return;
// Arrow functions inherit their parent's environment
if (path.isArrowFunctionExpression()) return;
path.skip();
},

"Method|ClassProperty"(path: NodePath<t.Method | t.ClassProperty>) {
skipAllButComputedKey(path);
},
};

const visitor = traverse.visitors.merge([
const visitor = traverse.visitors.merge<
HandlerState<ReplaceState> & ReplaceState
>([
environmentVisitor,
{
Super(path, state) {
Expand Down
1 change: 1 addition & 0 deletions packages/babel-plugin-transform-classes/package.json
Expand Up @@ -15,6 +15,7 @@
"main": "./lib/index.js",
"dependencies": {
"@babel/helper-annotate-as-pure": "workspace:^",
"@babel/helper-environment-visitor": "workspace:^",
"@babel/helper-function-name": "workspace:^",
"@babel/helper-optimise-call-expression": "workspace:^",
"@babel/helper-plugin-utils": "workspace:^",
Expand Down
5 changes: 2 additions & 3 deletions packages/babel-plugin-transform-classes/src/transformClass.ts
@@ -1,8 +1,7 @@
import type { NodePath, Visitor } from "@babel/traverse";
import nameFunction from "@babel/helper-function-name";
import ReplaceSupers, {
environmentVisitor,
} from "@babel/helper-replace-supers";
import ReplaceSupers from "@babel/helper-replace-supers";
import environmentVisitor from "@babel/helper-environment-visitor";
import optimiseCall from "@babel/helper-optimise-call-expression";
import { traverse, template, types as t } from "@babel/core";
import annotateAsPure from "@babel/helper-annotate-as-pure";
Expand Down
1 change: 1 addition & 0 deletions packages/babel-traverse/package.json
Expand Up @@ -18,6 +18,7 @@
"dependencies": {
"@babel/code-frame": "workspace:^",
"@babel/generator": "workspace:^",
"@babel/helper-environment-visitor": "workspace:^",
"@babel/helper-function-name": "workspace:^",
"@babel/helper-hoist-variables": "workspace:^",
"@babel/helper-split-export-declaration": "workspace:^",
Expand Down