diff --git a/packages/babel-generator/src/generators/classes.js b/packages/babel-generator/src/generators/classes.js index 1649cdee39..da1b14f78e 100644 --- a/packages/babel-generator/src/generators/classes.js +++ b/packages/babel-generator/src/generators/classes.js @@ -117,6 +117,21 @@ export function ClassProperty(node: Object) { this.semicolon(); } +export function ClassPrivateProperty(node: Object) { + if (node.static) { + this.word("static"); + this.space(); + } + this.print(node.key, node); + if (node.value) { + this.space(); + this.token("="); + this.space(); + this.print(node.value, node); + } + this.semicolon(); +} + export function ClassMethod(node: Object) { this._classMethodHead(node); this.space(); diff --git a/packages/babel-generator/src/generators/expressions.js b/packages/babel-generator/src/generators/expressions.js index e85eec6538..2a7fc66e2e 100644 --- a/packages/babel-generator/src/generators/expressions.js +++ b/packages/babel-generator/src/generators/expressions.js @@ -254,3 +254,8 @@ export function MetaProperty(node: Object) { this.token("."); this.print(node.property, node); } + +export function PrivateName(node: Object) { + this.token("#"); + this.print(node.id, node); +} diff --git a/packages/babel-generator/test/fixtures/types/ClassBody-ClassProperty/input.js b/packages/babel-generator/test/fixtures/types/ClassBody-ClassProperty/input.js index 41176ad01b..6256942ca5 100644 --- a/packages/babel-generator/test/fixtures/types/ClassBody-ClassProperty/input.js +++ b/packages/babel-generator/test/fixtures/types/ClassBody-ClassProperty/input.js @@ -26,6 +26,11 @@ class Foo { async; foo; bar; foo = 0; bar = 1; + + #foo; + #foo = 1; + static #foo; + static #foo = Foo.#foo; } class A1 { diff --git a/packages/babel-generator/test/fixtures/types/ClassBody-ClassProperty/options.json b/packages/babel-generator/test/fixtures/types/ClassBody-ClassProperty/options.json index 24551b8170..a437d2af54 100644 --- a/packages/babel-generator/test/fixtures/types/ClassBody-ClassProperty/options.json +++ b/packages/babel-generator/test/fixtures/types/ClassBody-ClassProperty/options.json @@ -1 +1 @@ -{ "plugins": ["classProperties"] } \ No newline at end of file +{ "plugins": ["classProperties", "classPrivateProperties"] } diff --git a/packages/babel-generator/test/fixtures/types/ClassBody-ClassProperty/output.js b/packages/babel-generator/test/fixtures/types/ClassBody-ClassProperty/output.js index 0c686caa32..905fe31bdb 100644 --- a/packages/babel-generator/test/fixtures/types/ClassBody-ClassProperty/output.js +++ b/packages/babel-generator/test/fixtures/types/ClassBody-ClassProperty/output.js @@ -26,6 +26,10 @@ class Foo { bar; foo = 0; bar = 1; + #foo; + #foo = 1; + static #foo; + static #foo = Foo.#foo; } class A1 { @@ -65,4 +69,4 @@ class A6 { class A7 { static get static() {} -} \ No newline at end of file +} diff --git a/packages/babel-helper-define-map/src/index.js b/packages/babel-helper-define-map/src/index.js index 850bd51adb..ff588c1ce3 100644 --- a/packages/babel-helper-define-map/src/index.js +++ b/packages/babel-helper-define-map/src/index.js @@ -61,7 +61,7 @@ export function push( key = t.toComputedKey(node, node.key); } - if (t.isObjectProperty(node) || t.isClassProperty(node)) { + if (t.isProperty(node)) { value = node.value; } else if (t.isObjectMethod(node) || t.isClassMethod(node)) { value = t.functionExpression( @@ -131,15 +131,12 @@ export function toClassObject(mutatorMap: Object): Object { const propNode = t.objectProperty(map._key, mapNode, map._computed); Object.keys(map).forEach(function(key) { - let node = map[key]; + const node = map[key]; if (key[0] === "_") return; - const inheritNode = node; - if (t.isClassMethod(node) || t.isClassProperty(node)) node = node.value; - const prop = t.objectProperty(t.identifier(key), node); - t.inheritsComments(prop, inheritNode); - t.removeComments(inheritNode); + t.inheritsComments(prop, node); + t.removeComments(node); mapNode.properties.push(prop); }); diff --git a/packages/babel-plugin-syntax-class-properties/src/index.js b/packages/babel-plugin-syntax-class-properties/src/index.js index 8f4ee10929..fc3f176e03 100644 --- a/packages/babel-plugin-syntax-class-properties/src/index.js +++ b/packages/babel-plugin-syntax-class-properties/src/index.js @@ -5,7 +5,7 @@ export default declare(api => { return { manipulateOptions(opts, parserOpts) { - parserOpts.plugins.push("classProperties"); + parserOpts.plugins.push("classProperties", "classPrivateProperties"); }, }; }); diff --git a/packages/babel-plugin-transform-parameters/src/rest.js b/packages/babel-plugin-transform-parameters/src/rest.js index aa3a8885fb..4f0817b857 100644 --- a/packages/babel-plugin-transform-parameters/src/rest.js +++ b/packages/babel-plugin-transform-parameters/src/rest.js @@ -46,7 +46,7 @@ const memberExpressionOptimisationVisitor = { path.skip(); }, - "Function|ClassProperty": function(path, state) { + Function(path, state) { // Detect whether any reference to rest is contained in nested functions to // determine if deopt is necessary. const oldNoOptimise = state.noOptimise; diff --git a/packages/babel-plugin-transform-parameters/test/fixtures/parameters/rest-arrow-functions/input.js b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/rest-arrow-functions/input.js index 80988d02b6..9cedf94796 100644 --- a/packages/babel-plugin-transform-parameters/test/fixtures/parameters/rest-arrow-functions/input.js +++ b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/rest-arrow-functions/input.js @@ -33,4 +33,4 @@ var innerclassproperties = (...args) => ( static args = args; args = args; } -); \ No newline at end of file +); diff --git a/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/destructure/output.js b/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/destructure/output.js index f13ad91a47..e33d215b94 100644 --- a/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/destructure/output.js +++ b/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/destructure/output.js @@ -1,8 +1,8 @@ class AnchorLink extends Component { render() { - const _props = this.props, - isExternal = _props.isExternal, - children = _props.children; + const _this$props = this.props, + isExternal = _this$props.isExternal, + children = _this$props.children; if (isExternal) { return {children}; diff --git a/packages/babel-traverse/src/path/conversion.js b/packages/babel-traverse/src/path/conversion.js index ca8b144942..b192b4d9d5 100644 --- a/packages/babel-traverse/src/path/conversion.js +++ b/packages/babel-traverse/src/path/conversion.js @@ -163,12 +163,13 @@ function hoistFunctionEnvironment( specCompliant = false, allowInsertArrow = true, ) { - const thisEnvFn = fnPath.findParent( - p => + const thisEnvFn = fnPath.findParent(p => { + return ( (p.isFunction() && !p.isArrowFunctionExpression()) || p.isProgram() || - p.isClassProperty({ static: false }), - ); + p.isClassProperty({ static: false }) + ); + }); const inConstructor = thisEnvFn && thisEnvFn.node.kind === "constructor"; if (thisEnvFn.isClassProperty()) { diff --git a/packages/babel-traverse/src/scope/index.js b/packages/babel-traverse/src/scope/index.js index a2055dd05a..4603f326e3 100644 --- a/packages/babel-traverse/src/scope/index.js +++ b/packages/babel-traverse/src/scope/index.js @@ -36,6 +36,10 @@ function gatherNodeParts(node: Object, parts: Array) { for (const prop of (node.properties: Array)) { gatherNodeParts(prop.key || prop.argument, parts); } + } else if (t.isPrivateName(node)) { + gatherNodeParts(node.id, parts); + } else if (t.isThisExpression(node)) { + parts.push("this"); } } @@ -622,7 +626,7 @@ export default class Scope { if (node.computed && !this.isPure(node.key, constantsOnly)) return false; if (node.kind === "get" || node.kind === "set") return false; return true; - } else if (t.isClassProperty(node) || t.isObjectProperty(node)) { + } else if (t.isProperty(node)) { if (node.computed && !this.isPure(node.key, constantsOnly)) return false; return this.isPure(node.value, constantsOnly); } else if (t.isUnaryExpression(node)) { diff --git a/packages/babel-types/README.md b/packages/babel-types/README.md index 51b2183103..aaee40e532 100644 --- a/packages/babel-types/README.md +++ b/packages/babel-types/README.md @@ -256,7 +256,7 @@ t.classBody(body) See also `t.isClassBody(node, opts)` and `t.assertClassBody(node, opts)`. - - `body`: `Array` (required) + - `body`: `Array` (required) --- @@ -343,6 +343,20 @@ Aliases: `Function`, `Scopable`, `BlockParent`, `FunctionParent`, `Method` --- +### classPrivateProperty +```javascript +t.classPrivateProperty(key, value) +``` + +See also `t.isClassPrivateProperty(node, opts)` and `t.assertClassPrivateProperty(node, opts)`. + +Aliases: `Property`, `Private` + + - `key`: `PrivateName` (required) + - `value`: `Expression` (default: `null`) + +--- + ### classProperty ```javascript t.classProperty(key, value, typeAnnotation, decorators, computed) @@ -1636,6 +1650,19 @@ Aliases: `Expression`, `ExpressionWrapper` --- +### privateName +```javascript +t.privateName(id) +``` + +See also `t.isPrivateName(node, opts)` and `t.assertPrivateName(node, opts)`. + +Aliases: `Private` + + - `id`: `Identifier` (required) + +--- + ### program ```javascript t.program(body, directives, sourceType) diff --git a/packages/babel-types/src/asserts/generated/index.js b/packages/babel-types/src/asserts/generated/index.js index efae53080e..bfc46770eb 100644 --- a/packages/babel-types/src/asserts/generated/index.js +++ b/packages/babel-types/src/asserts/generated/index.js @@ -663,6 +663,12 @@ export function assertOptionalCallExpression( ): void { assert("OptionalCallExpression", node, opts); } +export function assertClassPrivateProperty( + node: Object, + opts?: Object = {}, +): void { + assert("ClassPrivateProperty", node, opts); +} export function assertImport(node: Object, opts?: Object = {}): void { assert("Import", node, opts); } @@ -684,6 +690,9 @@ export function assertExportNamespaceSpecifier( ): void { assert("ExportNamespaceSpecifier", node, opts); } +export function assertPrivateName(node: Object, opts?: Object = {}): void { + assert("PrivateName", node, opts); +} export function assertTSParameterProperty( node: Object, opts?: Object = {}, @@ -1059,6 +1068,9 @@ export function assertFlowPredicate(node: Object, opts?: Object = {}): void { export function assertJSX(node: Object, opts?: Object = {}): void { assert("JSX", node, opts); } +export function assertPrivate(node: Object, opts?: Object = {}): void { + assert("Private", node, opts); +} export function assertTSTypeElement(node: Object, opts?: Object = {}): void { assert("TSTypeElement", node, opts); } diff --git a/packages/babel-types/src/builders/generated/index.js b/packages/babel-types/src/builders/generated/index.js index 1ec3b17766..b3c07aa47d 100644 --- a/packages/babel-types/src/builders/generated/index.js +++ b/packages/babel-types/src/builders/generated/index.js @@ -604,6 +604,10 @@ export function OptionalCallExpression(...args: Array): Object { return builder("OptionalCallExpression", ...args); } export { OptionalCallExpression as optionalCallExpression }; +export function ClassPrivateProperty(...args: Array): Object { + return builder("ClassPrivateProperty", ...args); +} +export { ClassPrivateProperty as classPrivateProperty }; export function Import(...args: Array): Object { return builder("Import", ...args); } @@ -624,6 +628,10 @@ export function ExportNamespaceSpecifier(...args: Array): Object { return builder("ExportNamespaceSpecifier", ...args); } export { ExportNamespaceSpecifier as exportNamespaceSpecifier }; +export function PrivateName(...args: Array): Object { + return builder("PrivateName", ...args); +} +export { PrivateName as privateName }; export function TSParameterProperty(...args: Array): Object { return builder("TSParameterProperty", ...args); } diff --git a/packages/babel-types/src/constants/generated/index.js b/packages/babel-types/src/constants/generated/index.js index ea64f77d1e..eedb7038ed 100644 --- a/packages/babel-types/src/constants/generated/index.js +++ b/packages/babel-types/src/constants/generated/index.js @@ -46,5 +46,6 @@ export const FLOWBASEANNOTATION_TYPES = export const FLOWDECLARATION_TYPES = FLIPPED_ALIAS_KEYS["FlowDeclaration"]; export const FLOWPREDICATE_TYPES = FLIPPED_ALIAS_KEYS["FlowPredicate"]; export const JSX_TYPES = FLIPPED_ALIAS_KEYS["JSX"]; +export const PRIVATE_TYPES = FLIPPED_ALIAS_KEYS["Private"]; export const TSTYPEELEMENT_TYPES = FLIPPED_ALIAS_KEYS["TSTypeElement"]; export const TSTYPE_TYPES = FLIPPED_ALIAS_KEYS["TSType"]; diff --git a/packages/babel-types/src/definitions/core.js b/packages/babel-types/src/definitions/core.js index 0bd5c2ae33..ab1e8b70b3 100644 --- a/packages/babel-types/src/definitions/core.js +++ b/packages/babel-types/src/definitions/core.js @@ -503,7 +503,7 @@ defineType("MemberExpression", { }, property: { validate: (function() { - const normal = assertNodeType("Identifier"); + const normal = assertNodeType("Identifier", "PrivateName"); const computed = assertNodeType("Expression"); return function(node, key, val) { diff --git a/packages/babel-types/src/definitions/es2015.js b/packages/babel-types/src/definitions/es2015.js index f5c67810f3..26c0d4a344 100644 --- a/packages/babel-types/src/definitions/es2015.js +++ b/packages/babel-types/src/definitions/es2015.js @@ -88,6 +88,7 @@ defineType("ClassBody", { assertNodeType( "ClassMethod", "ClassProperty", + "ClassPrivateProperty", "TSDeclareMethod", "TSIndexSignature", ), diff --git a/packages/babel-types/src/definitions/experimental.js b/packages/babel-types/src/definitions/experimental.js index c9d7b1a301..7b22fa0a04 100644 --- a/packages/babel-types/src/definitions/experimental.js +++ b/packages/babel-types/src/definitions/experimental.js @@ -115,6 +115,21 @@ defineType("OptionalCallExpression", { }, }); +defineType("ClassPrivateProperty", { + visitor: ["key", "value"], + builder: ["key", "value"], + aliases: ["Property", "Private"], + fields: { + key: { + validate: assertNodeType("PrivateName"), + }, + value: { + validate: assertNodeType("Expression"), + optional: true, + }, + }, +}); + defineType("Import", { aliases: ["Expression"], }); @@ -157,3 +172,13 @@ defineType("ExportNamespaceSpecifier", { }, }, }); + +defineType("PrivateName", { + visitor: ["id"], + aliases: ["Private"], + fields: { + id: { + validate: assertNodeType("Identifier"), + }, + }, +}); diff --git a/packages/babel-types/src/validators/generated/index.js b/packages/babel-types/src/validators/generated/index.js index 874d8f6508..48729135bf 100644 --- a/packages/babel-types/src/validators/generated/index.js +++ b/packages/babel-types/src/validators/generated/index.js @@ -494,6 +494,9 @@ export function isOptionalMemberExpression( export function isOptionalCallExpression(node: Object, opts?: Object): boolean { return is("OptionalCallExpression", node, opts); } +export function isClassPrivateProperty(node: Object, opts?: Object): boolean { + return is("ClassPrivateProperty", node, opts); +} export function isImport(node: Object, opts?: Object): boolean { return is("Import", node, opts); } @@ -512,6 +515,9 @@ export function isExportNamespaceSpecifier( ): boolean { return is("ExportNamespaceSpecifier", node, opts); } +export function isPrivateName(node: Object, opts?: Object): boolean { + return is("PrivateName", node, opts); +} export function isTSParameterProperty(node: Object, opts?: Object): boolean { return is("TSParameterProperty", node, opts); } @@ -821,6 +827,9 @@ export function isFlowPredicate(node: Object, opts?: Object): boolean { export function isJSX(node: Object, opts?: Object): boolean { return is("JSX", node, opts); } +export function isPrivate(node: Object, opts?: Object): boolean { + return is("Private", node, opts); +} export function isTSTypeElement(node: Object, opts?: Object): boolean { return is("TSTypeElement", node, opts); } diff --git a/packages/babel-types/src/validators/isReferenced.js b/packages/babel-types/src/validators/isReferenced.js index 1944665e9d..076bbb272d 100644 --- a/packages/babel-types/src/validators/isReferenced.js +++ b/packages/babel-types/src/validators/isReferenced.js @@ -76,8 +76,9 @@ export default function isReferenced(node: Object, parent: Object): boolean { // yes: class { [NODE] = value; } // yes: class { key = NODE; } case "ClassProperty": + case "ClassPrivateProperty": if (parent.key === node) { - return parent.computed; + return !!parent.computed; } else { return parent.value === node; }