Implement assumptions defined in the babel/rfcs#5 RFC

- `mutableTemplateObject` and `ignoreToPrimitiveHint` (#12408)
- `setClassMethods` (#12407)
- `setComputedProperties` (#12490)
- `ignoreFunctionLength` (#12491)
- `noDocumentAll` (#12481)
- `iterableIsArray` and `arrayLikeIsIterable` (#12489)
- `pureGetters` (#12504)
- `skipForOfIteratorClosing` (#12496)
- `objectRestNoSymbols`, `setSpreadProperties` and `pureGetters` (#12505)
- `noNewArrows` (#12613, #12793)
- `setPublicClassFields` and `privateFieldsAsProperties` (#12497)
- `constantReexports` and `enumerableModuleMeta` (#12618)
- `constantSuper`, `superIsCallableConstructor` and `noClassCalls` (#12726)

Co-authored-by: Justin Ridgewell <justin@ridgewell.name>
Co-authored-by: Huáng Jùnliàng <JLHwung@users.noreply.github.com>
This commit is contained in:
Nicolò Ribaudo 2020-12-11 20:28:38 +01:00
parent 7965c15557
commit 6ef7b51a11
586 changed files with 5747 additions and 201 deletions

View File

@ -332,8 +332,26 @@ type EnvPath = $ReadOnly<{
export type NestingPath = RootPath | OverridesPath | EnvPath;
export const assumptionsNames = new Set<string>([
"arrayLikeIsIterable",
"constantReexports",
"constantSuper",
"enumerableModuleMeta",
"ignoreFunctionLength",
"ignoreToPrimitiveHint",
"iterableIsArray",
"mutableTemplateObject",
"noClassCalls",
"noDocumentAll",
"noNewArrows",
"objectRestNoSymbols",
"privateFieldsAsProperties",
"pureGetters",
"setClassMethods",
"setComputedProperties",
"setPublicClassFields",
"setSpreadProperties",
"skipForOfIteratorClosing",
"superIsCallableConstructor",
]);
function getSource(loc: NestingPath): OptionsSource {

View File

@ -35,11 +35,16 @@ export function buildPrivateNamesMap(props) {
return privateNamesMap;
}
export function buildPrivateNamesNodes(privateNamesMap, loose, state) {
export function buildPrivateNamesNodes(
privateNamesMap,
privateFieldsAsProperties,
state,
) {
const initNodes = [];
for (const [name, value] of privateNamesMap) {
// In loose mode, both static and instance fields are transpiled using a
// When the privateFieldsAsProperties assumption is enabled,
// both static and instance fields are transpiled using a
// secret non-enumerable property. Hence, we also need to generate that
// key (using the classPrivateFieldLooseKey helper).
// In spec mode, only instance fields need a "private name" initializer
@ -48,7 +53,7 @@ export function buildPrivateNamesNodes(privateNamesMap, loose, state) {
const { static: isStatic, method: isMethod, getId, setId } = value;
const isAccessor = getId || setId;
const id = t.cloneNode(value.id);
if (loose) {
if (privateFieldsAsProperties) {
initNodes.push(
template.statement.ast`
var ${id} = ${state.addHelper("classPrivateFieldLooseKey")}("${name}")
@ -149,13 +154,13 @@ const privateInVisitor = privateNameVisitorFactory({
if (operator !== "in") return;
if (!path.get("left").isPrivateName()) return;
const { loose, privateNamesMap, redeclared } = this;
const { privateFieldsAsProperties, privateNamesMap, redeclared } = this;
const { name } = left.id;
if (!privateNamesMap.has(name)) return;
if (redeclared && redeclared.includes(name)) return;
if (loose) {
if (privateFieldsAsProperties) {
const { id } = privateNamesMap.get(name);
path.replaceWith(template.expression.ast`
Object.prototype.hasOwnProperty.call(${right}, ${t.cloneNode(id)})
@ -373,13 +378,15 @@ export function transformPrivateNamesUsage(
ref,
path,
privateNamesMap,
loose,
{ privateFieldsAsProperties },
state,
) {
if (!privateNamesMap.size) return;
const body = path.get("body");
const handler = loose ? privateNameHandlerLoose : privateNameHandlerSpec;
const handler = privateFieldsAsProperties
? privateNameHandlerLoose
: privateNameHandlerSpec;
memberExpressionToFunctions(body, privateNameVisitor, {
privateNamesMap,
@ -391,7 +398,7 @@ export function transformPrivateNamesUsage(
privateNamesMap,
classRef: ref,
file: state,
loose,
privateFieldsAsProperties,
});
}
@ -573,7 +580,11 @@ function buildPrivateStaticMethodInitLoose(ref, prop, state, privateNamesMap) {
`;
}
function buildPrivateMethodDeclaration(prop, privateNamesMap, loose = false) {
function buildPrivateMethodDeclaration(
prop,
privateNamesMap,
privateFieldsAsProperties = false,
) {
const privateName = privateNamesMap.get(prop.node.key.id.name);
const {
id,
@ -613,7 +624,7 @@ function buildPrivateMethodDeclaration(prop, privateNamesMap, loose = false) {
t.variableDeclarator(setId, methodValue),
]);
}
if (isStatic && !loose) {
if (isStatic && !privateFieldsAsProperties) {
return t.variableDeclaration("var", [
t.variableDeclarator(
t.cloneNode(id),
@ -637,12 +648,12 @@ const thisContextVisitor = traverse.visitors.merge([
environmentVisitor,
]);
function replaceThisContext(path, ref, superRef, file, loose) {
function replaceThisContext(path, ref, superRef, file, constantSuper) {
const state = { classRef: ref, needsClassRef: false };
const replacer = new ReplaceSupers({
methodPath: path,
isLoose: loose,
constantSuper,
superRef,
file,
refToPreserve: ref,
@ -666,7 +677,9 @@ export function buildFieldsInitNodes(
props,
privateNamesMap,
state,
loose,
setPublicClassFields,
privateFieldsAsProperties,
constantSuper,
) {
const staticNodes = [];
const instanceNodes = [];
@ -683,39 +696,45 @@ export function buildFieldsInitNodes(
const isMethod = !isField;
if (isStatic || (isMethod && isPrivate)) {
const replaced = replaceThisContext(prop, ref, superRef, state, loose);
const replaced = replaceThisContext(
prop,
ref,
superRef,
state,
constantSuper,
);
needsClassRef = needsClassRef || replaced;
}
switch (true) {
case isStatic && isPrivate && isField && loose:
case isStatic && isPrivate && isField && privateFieldsAsProperties:
needsClassRef = true;
staticNodes.push(
buildPrivateFieldInitLoose(t.cloneNode(ref), prop, privateNamesMap),
);
break;
case isStatic && isPrivate && isField && !loose:
case isStatic && isPrivate && isField && !privateFieldsAsProperties:
needsClassRef = true;
staticNodes.push(
buildPrivateStaticFieldInitSpec(prop, privateNamesMap),
);
break;
case isStatic && isPublic && isField && loose:
case isStatic && isPublic && isField && setPublicClassFields:
needsClassRef = true;
staticNodes.push(buildPublicFieldInitLoose(t.cloneNode(ref), prop));
break;
case isStatic && isPublic && isField && !loose:
case isStatic && isPublic && isField && !setPublicClassFields:
needsClassRef = true;
staticNodes.push(
buildPublicFieldInitSpec(t.cloneNode(ref), prop, state),
);
break;
case isInstance && isPrivate && isField && loose:
case isInstance && isPrivate && isField && privateFieldsAsProperties:
instanceNodes.push(
buildPrivateFieldInitLoose(t.thisExpression(), prop, privateNamesMap),
);
break;
case isInstance && isPrivate && isField && !loose:
case isInstance && isPrivate && isField && !privateFieldsAsProperties:
instanceNodes.push(
buildPrivateInstanceFieldInitSpec(
t.thisExpression(),
@ -724,7 +743,7 @@ export function buildFieldsInitNodes(
),
);
break;
case isInstance && isPrivate && isMethod && loose:
case isInstance && isPrivate && isMethod && privateFieldsAsProperties:
instanceNodes.unshift(
buildPrivateMethodInitLoose(
t.thisExpression(),
@ -733,10 +752,14 @@ export function buildFieldsInitNodes(
),
);
staticNodes.push(
buildPrivateMethodDeclaration(prop, privateNamesMap, loose),
buildPrivateMethodDeclaration(
prop,
privateNamesMap,
privateFieldsAsProperties,
),
);
break;
case isInstance && isPrivate && isMethod && !loose:
case isInstance && isPrivate && isMethod && !privateFieldsAsProperties:
instanceNodes.unshift(
buildPrivateInstanceMethodInitSpec(
t.thisExpression(),
@ -745,19 +768,27 @@ export function buildFieldsInitNodes(
),
);
staticNodes.push(
buildPrivateMethodDeclaration(prop, privateNamesMap, loose),
buildPrivateMethodDeclaration(
prop,
privateNamesMap,
privateFieldsAsProperties,
),
);
break;
case isStatic && isPrivate && isMethod && !loose:
case isStatic && isPrivate && isMethod && !privateFieldsAsProperties:
needsClassRef = true;
staticNodes.push(
buildPrivateStaticFieldInitSpec(prop, privateNamesMap),
);
staticNodes.unshift(
buildPrivateMethodDeclaration(prop, privateNamesMap, loose),
buildPrivateMethodDeclaration(
prop,
privateNamesMap,
privateFieldsAsProperties,
),
);
break;
case isStatic && isPrivate && isMethod && loose:
case isStatic && isPrivate && isMethod && privateFieldsAsProperties:
needsClassRef = true;
staticNodes.push(
buildPrivateStaticMethodInitLoose(
@ -768,13 +799,17 @@ export function buildFieldsInitNodes(
),
);
staticNodes.unshift(
buildPrivateMethodDeclaration(prop, privateNamesMap, loose),
buildPrivateMethodDeclaration(
prop,
privateNamesMap,
privateFieldsAsProperties,
),
);
break;
case isInstance && isPublic && isField && loose:
case isInstance && isPublic && isField && setPublicClassFields:
instanceNodes.push(buildPublicFieldInitLoose(t.thisExpression(), prop));
break;
case isInstance && isPublic && isField && !loose:
case isInstance && isPublic && isField && !setPublicClassFields:
instanceNodes.push(
buildPublicFieldInitSpec(t.thisExpression(), prop, state),
);

View File

@ -36,7 +36,38 @@ export function createClassFeaturePlugin({
feature,
loose,
manipulateOptions,
// TODO(Babel 8): Remove the default falue
api = { assumption: () => {} },
}) {
const setPublicClassFields = api.assumption("setPublicClassFields");
const privateFieldsAsProperties = api.assumption("privateFieldsAsProperties");
const constantSuper = api.assumption("constantSuper");
if (loose) {
const explicit = [];
if (setPublicClassFields !== undefined) {
explicit.push(`"setPublicClassFields"`);
}
if (privateFieldsAsProperties !== undefined) {
explicit.push(`"privateFieldsAsProperties"`);
}
if (explicit.length !== 0) {
console.warn(
`[${name}]: You are using the "loose: true" option and you are` +
` explicitly setting a value for the ${explicit.join(" and ")}` +
` assumption${explicit.length > 1 ? "s" : ""}. The "loose" option` +
` can cause incompatibilities with the other class features` +
` plugins, so it's recommended that you replace it with the` +
` following top-level option:\n` +
`\t"assumptions": {\n` +
`\t\t"setPublicClassFields": true,\n` +
`\t\t"privateFieldsAsProperties": true\n` +
`\t}`,
);
}
}
return {
name,
manipulateOptions,
@ -151,11 +182,17 @@ export function createClassFeaturePlugin({
const privateNamesMap = buildPrivateNamesMap(props);
const privateNamesNodes = buildPrivateNamesNodes(
privateNamesMap,
loose,
privateFieldsAsProperties ?? loose,
state,
);
transformPrivateNamesUsage(ref, path, privateNamesMap, loose, state);
transformPrivateNamesUsage(
ref,
path,
privateNamesMap,
{ privateFieldsAsProperties: privateFieldsAsProperties ?? loose },
state,
);
let keysNodes, staticNodes, instanceNodes, wrapClass;
@ -175,7 +212,9 @@ export function createClassFeaturePlugin({
props,
privateNamesMap,
state,
loose,
setPublicClassFields ?? loose,
privateFieldsAsProperties ?? loose,
constantSuper ?? loose,
));
}

View File

@ -0,0 +1,9 @@
{
"validateLogs": true,
"plugins": [
["proposal-class-properties", { "loose": true }]
],
"assumptions": {
"setPublicClassFields": true
}
}

View File

@ -0,0 +1,6 @@
class A {
constructor() {
this.foo = void 0;
}
}

View File

@ -0,0 +1,5 @@
[proposal-class-properties]: You are using the "loose: true" option and you are explicitly setting a value for the "setPublicClassFields" assumption. The "loose" option can cause incompatibilities with the other class features plugins, so it's recommended that you replace it with the following top-level option:
"assumptions": {
"setPublicClassFields": true,
"privateFieldsAsProperties": true
}

View File

@ -27,14 +27,19 @@ export { hasExports, isSideEffectImport, isModule, rewriteThis };
export function rewriteModuleStatementsAndPrepareHeader(
path: NodePath,
{
// TODO(Babel 8): Remove this
loose,
exportName,
strict,
allowTopLevelThis,
strictMode,
loose,
noInterop,
lazy,
esNamespaceOnly,
constantReexports = loose,
enumerableModuleMeta = loose,
},
) {
assert(isModule(path), "Cannot process module statements in a script");
@ -42,7 +47,7 @@ export function rewriteModuleStatementsAndPrepareHeader(
const meta = normalizeAndLoadModuleMetadata(path, exportName, {
noInterop,
loose,
initializeReexports: constantReexports,
lazy,
esNamespaceOnly,
});
@ -67,7 +72,7 @@ export function rewriteModuleStatementsAndPrepareHeader(
const headers = [];
if (hasExports(meta) && !strict) {
headers.push(buildESModuleHeader(meta, loose /* enumerable */));
headers.push(buildESModuleHeader(meta, enumerableModuleMeta));
}
const nameList = buildExportNameListDeclaration(path, meta);
@ -78,7 +83,9 @@ export function rewriteModuleStatementsAndPrepareHeader(
}
// Create all of the statically known named exports.
headers.push(...buildExportInitializationStatements(path, meta, loose));
headers.push(
...buildExportInitializationStatements(path, meta, constantReexports),
);
return { meta, headers };
}
@ -128,7 +135,7 @@ export function wrapInterop(
export function buildNamespaceInitStatements(
metadata: ModuleMetadata,
sourceMetadata: SourceModuleMetadata,
loose: boolean = false,
constantReexports: boolean = false,
) {
const statements = [];
@ -146,8 +153,8 @@ export function buildNamespaceInitStatements(
}),
);
}
if (loose) {
statements.push(...buildReexportsFromMeta(metadata, sourceMetadata, loose));
if (constantReexports) {
statements.push(...buildReexportsFromMeta(metadata, sourceMetadata, true));
}
for (const exportName of sourceMetadata.reexportNamespace) {
// Assign export to namespace object.
@ -172,7 +179,7 @@ export function buildNamespaceInitStatements(
const statement = buildNamespaceReexport(
metadata,
t.cloneNode(srcNamespace),
loose,
constantReexports,
);
statement.loc = sourceMetadata.reexportAll.loc;
@ -183,8 +190,8 @@ export function buildNamespaceInitStatements(
}
const ReexportTemplate = {
loose: template.statement`EXPORTS.EXPORT_NAME = NAMESPACE_IMPORT;`,
looseComputed: template.statement`EXPORTS["EXPORT_NAME"] = NAMESPACE_IMPORT;`,
constant: template.statement`EXPORTS.EXPORT_NAME = NAMESPACE_IMPORT;`,
constantComputed: template.statement`EXPORTS["EXPORT_NAME"] = NAMESPACE_IMPORT;`,
spec: template`
Object.defineProperty(EXPORTS, "EXPORT_NAME", {
enumerable: true,
@ -198,7 +205,7 @@ const ReexportTemplate = {
const buildReexportsFromMeta = (
meta: ModuleMetadata,
metadata: SourceModuleMetadata,
loose,
constantReexports: boolean,
) => {
const namespace = metadata.lazy
? t.callExpression(t.identifier(metadata.name), [])
@ -224,11 +231,11 @@ const buildReexportsFromMeta = (
EXPORT_NAME: exportName,
NAMESPACE_IMPORT,
};
if (loose) {
if (constantReexports) {
if (stringSpecifiers.has(exportName)) {
return ReexportTemplate.looseComputed(astNodes);
return ReexportTemplate.constantComputed(astNodes);
} else {
return ReexportTemplate.loose(astNodes);
return ReexportTemplate.constant(astNodes);
}
} else {
return ReexportTemplate.spec(astNodes);
@ -241,9 +248,9 @@ const buildReexportsFromMeta = (
*/
function buildESModuleHeader(
metadata: ModuleMetadata,
enumerable: boolean = false,
enumerableModuleMeta: boolean = false,
) {
return (enumerable
return (enumerableModuleMeta
? template.statement`
EXPORTS.__esModule = true;
`
@ -257,8 +264,8 @@ function buildESModuleHeader(
/**
* Create a re-export initialization loop for a specific imported namespace.
*/
function buildNamespaceReexport(metadata, namespace, loose) {
return (loose
function buildNamespaceReexport(metadata, namespace, constantReexports) {
return (constantReexports
? template.statement`
Object.keys(NAMESPACE).forEach(function(key) {
if (key === "default" || key === "__esModule") return;
@ -347,7 +354,7 @@ function buildExportNameListDeclaration(
function buildExportInitializationStatements(
programPath: NodePath,
metadata: ModuleMetadata,
loose: boolean = false,
constantReexports: boolean = false,
) {
const initStatements = [];
@ -365,8 +372,8 @@ function buildExportInitializationStatements(
}
for (const data of metadata.source.values()) {
if (!loose) {
initStatements.push(...buildReexportsFromMeta(metadata, data, loose));
if (!constantReexports) {
initStatements.push(...buildReexportsFromMeta(metadata, data, false));
}
for (const exportName of data.reexportNamespace) {
exportNames.push(exportName);

View File

@ -86,7 +86,7 @@ export default function normalizeModuleAndLoadMetadata(
exportName?: string,
{
noInterop = false,
loose = false,
initializeReexports = false,
lazy = false,
esNamespaceOnly = false,
} = {},
@ -100,10 +100,7 @@ export default function normalizeModuleAndLoadMetadata(
const { local, source, hasExports } = getModuleMetadata(
programPath,
{
loose,
lazy,
},
{ initializeReexports, lazy },
stringSpecifiers,
);
@ -170,12 +167,15 @@ function getExportSpecifierName(
*/
function getModuleMetadata(
programPath: NodePath,
{ loose, lazy }: { loose: boolean, lazy: boolean },
{
lazy,
initializeReexports,
}: { lazy: boolean, initializeReexports: boolean },
stringSpecifiers: Set<string>,
) {
const localData = getLocalExportMetadata(
programPath,
loose,
initializeReexports,
stringSpecifiers,
);
@ -361,7 +361,7 @@ function getModuleMetadata(
*/
function getLocalExportMetadata(
programPath: NodePath,
loose: boolean,
initializeReexports: boolean,
stringSpecifiers: Set<string>,
): Map<string, LocalExportMetadata> {
const bindingKindLookup = new Map();
@ -376,7 +376,7 @@ function getLocalExportMetadata(
if (child.node.declaration) {
child = child.get("declaration");
} else if (
loose &&
initializeReexports &&
child.node.source &&
child.get("source").isStringLiteral()
) {
@ -429,7 +429,10 @@ function getLocalExportMetadata(
};
programPath.get("body").forEach(child => {
if (child.isExportNamedDeclaration() && (loose || !child.node.source)) {
if (
child.isExportNamedDeclaration() &&
(initializeReexports || !child.node.source)
) {
if (child.node.declaration) {
const declaration = child.get("declaration");
const ids = declaration.getOuterBindingIdentifierPaths();

View File

@ -31,6 +31,7 @@ const awaitVisitor = {
export default function (
path: NodePath,
helpers: { wrapAsync: Object, wrapAwait: Object },
noNewArrows?: boolean,
) {
path.traverse(awaitVisitor, {
wrapAwait: helpers.wrapAwait,
@ -41,7 +42,7 @@ export default function (
path.node.async = false;
path.node.generator = true;
wrapFunction(path, t.cloneNode(helpers.wrapAsync));
wrapFunction(path, t.cloneNode(helpers.wrapAsync), noNewArrows);
const isProperty =
path.isObjectMethod() ||

View File

@ -255,7 +255,7 @@ const looseHandlers = {
type ReplaceSupersOptionsBase = {|
methodPath: NodePath,
superRef: Object,
isLoose: boolean,
constantSuper: boolean,
file: any,
// objectRef might have been shadowed in child scopes,
// in that case, we need to rename related variables.
@ -284,13 +284,16 @@ export default class ReplaceSupers {
this.file = opts.file;
this.superRef = opts.superRef;
this.isLoose = opts.isLoose;
this.constantSuper = process.env.BABEL_8_BREAKING
? opts.constantSuper
: // Fallback to isLoose for backward compatibility
opts.constantSuper ?? (opts: any).isLoose;
this.opts = opts;
}
declare file: HubInterface;
declare isDerivedConstructor: boolean;
declare isLoose: boolean;
declare constantSuper: boolean;
declare isPrivateMethod: boolean;
declare isStatic: boolean;
declare methodPath: NodePath;
@ -309,7 +312,7 @@ export default class ReplaceSupers {
});
}
const handler = this.isLoose ? looseHandlers : specHandlers;
const handler = this.constantSuper ? looseHandlers : specHandlers;
memberExpressionToFunctions(this.methodPath, visitor, {
file: this.file,

View File

@ -57,7 +57,7 @@ function classOrObjectMethod(path: NodePath, callId: Object) {
.unwrapFunctionEnvironment();
}
function plainFunction(path: NodePath, callId: Object) {
function plainFunction(path: NodePath, callId: Object, noNewArrows: boolean) {
const node = path.node;
const isDeclaration = path.isFunctionDeclaration();
const functionId = node.id;
@ -68,7 +68,7 @@ function plainFunction(path: NodePath, callId: Object) {
: buildAnonymousExpressionWrapper;
if (path.isArrowFunctionExpression()) {
path.arrowFunctionToExpression();
path.arrowFunctionToExpression({ noNewArrows });
}
node.id = null;
@ -123,10 +123,15 @@ function plainFunction(path: NodePath, callId: Object) {
}
}
export default function wrapFunction(path: NodePath, callId: Object) {
export default function wrapFunction(
path: NodePath,
callId: Object,
// TODO(Babel 8): Consider defaulting to false for spec compliancy
noNewArrows: boolean = true,
) {
if (path.isMethod()) {
classOrObjectMethod(path, callId);
} else {
plainFunction(path, callId);
plainFunction(path, callId, noNewArrows);
}
}

View File

@ -70,6 +70,8 @@ export default declare(api => {
path.traverse(yieldStarVisitor, state);
// We don't need to pass the noNewArrows assumption, since
// async generators are never arrow functions.
remapAsyncToGenerator(path, {
wrapAsync: state.addHelper("wrapAsyncGenerator"),
wrapAwait: state.addHelper("awaitAsyncGenerator"),

View File

@ -12,6 +12,7 @@ export default declare((api, options) => {
return createClassFeaturePlugin({
name: "proposal-class-properties",
api,
feature: FEATURES.fields,
loose: options.loose,

View File

@ -0,0 +1,38 @@
const actualOrder = [];
const track = i => {
actualOrder.push(i);
return i;
};
class MyClass {
static [track(1)] = track(10);
[track(2)] = track(13);
get [track(3)]() {
return "foo";
}
set [track(4)](value) {
this.bar = value;
}
[track(5)] = track(14);
static [track(6)] = track(11);
static [track(7)] = track(12);
[track(8)]() {}
[track(9)] = track(15);
}
const inst = new MyClass();
const expectedOrder = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
expect(actualOrder).toEqual(expectedOrder);
expect(MyClass[1]).toBe(10);
expect(inst[2]).toBe(13);
expect(inst[3]).toBe("foo");
inst[4] = "baz";
expect(inst.bar).toBe("baz");
expect(inst[5]).toBe(14);
expect(MyClass[6]).toBe(11);
expect(MyClass[7]).toBe(12);
expect(typeof inst[8]).toBe("function");
expect(inst[9]).toBe(15);

View File

@ -0,0 +1,25 @@
const foo = "foo";
const bar = () => {};
const four = 4;
class MyClass {
static [one()] = "test";
static [2 * 4 + 7] = "247";
static [2 * four + 7] = "247";
static [2 * four + seven] = "247";
[null] = "null";
[undefined] = "undefined";
[void 0] = "void 0";
get ["whatever"]() {}
set ["whatever"](value) {}
get [computed()]() {}
set [computed()](value) {}
["test" + one]() {}
static [10]() {}
[/regex/] = "regex";
[foo] = "foo";
[bar] = "bar";
[baz] = "baz";
[`template`] = "template";
[`template${expression}`] = "template-with-expression";
}

View File

@ -0,0 +1,48 @@
let _one, _ref, _undefined, _computed, _computed2, _ref2, _ref3, _baz, _ref4;
const foo = "foo";
const bar = () => {};
const four = 4;
_one = one();
_ref = 2 * four + seven;
_undefined = undefined;
_computed = computed();
_computed2 = computed();
_ref2 = "test" + one;
_ref3 = /regex/;
_baz = baz;
_ref4 = `template${expression}`;
class MyClass {
constructor() {
this[null] = "null";
this[_undefined] = "undefined";
this[void 0] = "void 0";
this[_ref3] = "regex";
this[foo] = "foo";
this[bar] = "bar";
this[_baz] = "baz";
this[`template`] = "template";
this[_ref4] = "template-with-expression";
}
get ["whatever"]() {}
set ["whatever"](value) {}
get [_computed]() {}
set [_computed2](value) {}
[_ref2]() {}
static [10]() {}
}
MyClass[_one] = "test";
MyClass[2 * 4 + 7] = "247";
MyClass[2 * four + 7] = "247";
MyClass[_ref] = "247";

View File

@ -0,0 +1,11 @@
var foo = "bar";
class Foo {
bar = foo;
static bar = baz;
constructor() {
var foo = "foo";
var baz = "baz";
}
}

View File

@ -0,0 +1,12 @@
var foo = "bar";
class Foo {
constructor() {
this.bar = foo;
var _foo = "foo";
var baz = "baz";
}
}
Foo.bar = baz;

View File

@ -0,0 +1,3 @@
class Foo extends Bar {
bar = "foo";
}

View File

@ -0,0 +1,7 @@
class Foo extends Bar {
constructor(...args) {
super(...args);
this.bar = "foo";
}
}

View File

@ -0,0 +1,9 @@
class Child extends Parent {
constructor() {
super();
}
scopedFunctionWithThis = () => {
this.name = {};
}
}

View File

@ -0,0 +1,10 @@
{
"plugins": [
["external-helpers", { "helperVersion": "7.100.0" }],
"proposal-class-properties"
],
"presets": ["env"],
"assumptions": {
"setPublicClassFields": true
}
}

View File

@ -0,0 +1,22 @@
var Child = /*#__PURE__*/function (_Parent) {
"use strict";
babelHelpers.inherits(Child, _Parent);
var _super = babelHelpers.createSuper(Child);
function Child() {
var _this;
babelHelpers.classCallCheck(this, Child);
_this = _super.call(this);
_this.scopedFunctionWithThis = function () {
_this.name = {};
};
return _this;
}
return Child;
}(Parent);

View File

@ -0,0 +1,13 @@
function test(x) {
class F {
[x] = 1;
constructor() {}
}
x = 'deadbeef';
expect(new F().foo).toBe(1);
x = 'wrong';
expect(new F().foo).toBe(1);
}
test('foo');

View File

@ -0,0 +1,13 @@
function test(x) {
class F {
[x] = 1;
constructor() {}
}
x = 'deadbeef';
expect(new F().foo).toBe(1);
x = 'wrong';
expect(new F().foo).toBe(1);
}
test('foo');

View File

@ -0,0 +1,19 @@
function test(x) {
let _x;
_x = x;
class F {
constructor() {
this[_x] = 1;
}
}
x = 'deadbeef';
expect(new F().foo).toBe(1);
x = 'wrong';
expect(new F().foo).toBe(1);
}
test('foo');

View File

@ -0,0 +1,6 @@
class Foo {
constructor() {
this.bar = void 0;
}
}

View File

@ -0,0 +1,3 @@
class Foo {
bar = "foo";
}

View File

@ -0,0 +1,6 @@
class Foo {
constructor() {
this.bar = "foo";
}
}

View File

@ -0,0 +1,11 @@
export default param =>
class App {
static props = {
prop1: 'prop1',
prop2: 'prop2'
}
getParam() {
return param;
}
}

View File

@ -0,0 +1,13 @@
export default (param => {
var _class, _temp;
return _temp = _class = class App {
getParam() {
return param;
}
}, _class.props = {
prop1: 'prop1',
prop2: 'prop2'
}, _temp;
});

View File

@ -0,0 +1,10 @@
{
"plugins": [
["external-helpers", { "helperVersion": "7.100.0" }],
"proposal-class-properties",
"syntax-class-properties"
],
"assumptions": {
"setPublicClassFields": true
}
}

View File

@ -0,0 +1,7 @@
call(class {
static test = true
});
export default class {
static test = true
};

View File

@ -0,0 +1,6 @@
var _class, _temp;
call((_temp = _class = class {}, _class.test = true, _temp));
export default class _class2 {}
_class2.test = true;
;

View File

@ -0,0 +1,14 @@
function withContext(ComposedComponent) {
return class WithContext extends Component {
static propTypes = {
context: PropTypes.shape(
{
addCss: PropTypes.func,
setTitle: PropTypes.func,
setMeta: PropTypes.func,
}
),
};
};
}

View File

@ -0,0 +1,11 @@
function withContext(ComposedComponent) {
var _class, _temp;
return _temp = _class = class WithContext extends Component {}, _class.propTypes = {
context: PropTypes.shape({
addCss: PropTypes.func,
setTitle: PropTypes.func,
setMeta: PropTypes.func
})
}, _temp;
}

View File

@ -0,0 +1,17 @@
class MyClass {
myAsyncMethod = async () => {
console.log(this);
}
}
(class MyClass2 {
myAsyncMethod = async () => {
console.log(this);
}
})
export default class MyClass3 {
myAsyncMethod = async () => {
console.log(this);
}
}

View File

@ -0,0 +1,10 @@
{
"plugins": [
"external-helpers",
"transform-async-to-generator",
"proposal-class-properties"
],
"assumptions": {
"setPublicClassFields": true
}
}

View File

@ -0,0 +1,32 @@
class MyClass {
constructor() {
var _this = this;
this.myAsyncMethod = /*#__PURE__*/babelHelpers.asyncToGenerator(function* () {
console.log(_this);
});
}
}
(class MyClass2 {
constructor() {
var _this2 = this;
this.myAsyncMethod = /*#__PURE__*/babelHelpers.asyncToGenerator(function* () {
console.log(_this2);
});
}
});
export default class MyClass3 {
constructor() {
var _this3 = this;
this.myAsyncMethod = /*#__PURE__*/babelHelpers.asyncToGenerator(function* () {
console.log(_this3);
});
}
}

View File

@ -0,0 +1,7 @@
export class MyClass {
static property = value;
}
export default class MyClass2 {
static property = value;
}

View File

@ -0,0 +1,4 @@
export class MyClass {}
MyClass.property = value;
export default class MyClass2 {}
MyClass2.property = value;

View File

@ -0,0 +1,7 @@
var Foo = class {
static num = 0;
}
expect(Foo.num).toBe(0);
expect(Foo.num = 1).toBe(1);
expect(Foo.name).toBe("Foo");

View File

@ -0,0 +1,3 @@
var Foo = class {
static num = 0;
}

View File

@ -0,0 +1,3 @@
var _class, _temp;
var Foo = (_temp = _class = class Foo {}, _class.num = 0, _temp);

View File

@ -0,0 +1,9 @@
class A {
static prop = 1;
}
class B extends A {
static prop = 2;
static propA = super.prop;
static getPropA = () => super.prop;
}

View File

@ -0,0 +1,11 @@
{
"validateLogs": true,
"plugins": [
["external-helpers", { "helperVersion": "7.100.0" }],
["proposal-class-properties", { "loose": true }],
"syntax-class-properties"
],
"assumptions": {
"setPublicClassFields": true
}
}

View File

@ -0,0 +1,10 @@
class A {}
A.prop = 1;
class B extends A {}
B.prop = 2;
B.propA = A.prop;
B.getPropA = () => A.prop;

View File

@ -0,0 +1,5 @@
[proposal-class-properties]: You are using the "loose: true" option and you are explicitly setting a value for the "setPublicClassFields" assumption. The "loose" option can cause incompatibilities with the other class features plugins, so it's recommended that you replace it with the following top-level option:
"assumptions": {
"setPublicClassFields": true,
"privateFieldsAsProperties": true
}

View File

@ -0,0 +1,15 @@
class A {
static prop = 1;
}
class B extends A {
static prop = 2;
static propA = super.prop;
static getPropA = () => super.prop;
}
const { prop, propA, getPropA } = B;
expect(prop).toBe(2);
expect(propA).toBe(1);
expect(getPropA()).toBe(1);

View File

@ -0,0 +1,9 @@
class A {
static prop = 1;
}
class B extends A {
static prop = 2;
static propA = super.prop;
static getPropA = () => super.prop;
}

View File

@ -0,0 +1,10 @@
class A {}
A.prop = 1;
class B extends A {}
B.prop = 2;
B.propA = babelHelpers.get(babelHelpers.getPrototypeOf(B), "prop", B);
B.getPropA = () => babelHelpers.get(babelHelpers.getPrototypeOf(B), "prop", B);

View File

@ -0,0 +1,9 @@
class A {
static self = this;
static getA = () => this;
}
const { self, getA } = A;
expect(self).toBe(A);
expect(getA()).toBe(A);

View File

@ -0,0 +1,4 @@
class A {
static self = this;
static getA = () => this;
}

View File

@ -0,0 +1,5 @@
class A {}
A.self = A;
A.getA = () => A;

View File

@ -0,0 +1,6 @@
class Foo {
static num;
}
expect("num" in Foo).toBe(true);
expect(Foo.num).toBeUndefined();

View File

@ -0,0 +1,3 @@
class Foo {}
Foo.bar = void 0;

View File

@ -0,0 +1,9 @@
class Foo {
static num = 0;
static str = "foo";
}
expect(Foo.num).toBe(0);
expect(Foo.num = 1).toBe(1);
expect(Foo.str).toBe("foo");
expect(Foo.str = "bar").toBe("bar");

View File

@ -0,0 +1,3 @@
class Foo {
static bar = "foo";
}

View File

@ -0,0 +1,3 @@
class Foo {}
Foo.bar = "foo";

View File

@ -0,0 +1,9 @@
class A {
foo() {
return "bar";
}
}
class B extends A {
foo = super.foo();
}

View File

@ -0,0 +1,14 @@
class A {
foo() {
return "bar";
}
}
class B extends A {
constructor(...args) {
super(...args);
this.foo = super.foo();
}
}

View File

@ -0,0 +1,7 @@
class Foo extends Bar {
bar = "foo";
constructor() {
foo(super());
}
}

View File

@ -0,0 +1,8 @@
class Foo extends Bar {
constructor() {
var _temp;
foo((_temp = super(), this.bar = "foo", _temp));
}
}

View File

@ -0,0 +1,7 @@
class Foo extends Bar {
bar = "foo";
constructor() {
super();
}
}

View File

@ -0,0 +1,7 @@
class Foo extends Bar {
constructor() {
super();
this.bar = "foo";
}
}

View File

@ -0,0 +1,6 @@
class A {
force = force;
foo = super.method();
constructor(force) {}
}

View File

@ -0,0 +1,7 @@
class A {
constructor(_force) {
this.force = force;
this.foo = super.method();
}
}

View File

@ -50,6 +50,7 @@ export default declare((api, options) => {
return createClassFeaturePlugin({
name: "proposal-decorators",
api,
feature: FEATURES.decorators,
// loose: options.loose, Not supported

View File

@ -4,6 +4,7 @@ import { types as t, template } from "@babel/core";
export default declare((api, { loose = false }) => {
api.assertVersion(7);
const noDocumentAll = api.assumption("noDocumentAll") ?? loose;
return {
name: "proposal-nullish-coalescing-operator",
@ -38,7 +39,7 @@ export default declare((api, { loose = false }) => {
t.conditionalExpression(
// We cannot use `!= null` in spec mode because
// `document.all == null` and `document.all` is not "nullish".
loose
noDocumentAll
? t.binaryExpression("!=", assignment, t.nullLiteral())
: t.logicalExpression(
"&&",

View File

@ -0,0 +1,6 @@
{
"plugins": ["proposal-nullish-coalescing-operator"],
"assumptions": {
"noDocumentAll": true
}
}

View File

@ -0,0 +1,19 @@
expect(null ?? undefined).toBeUndefined(undefined);
expect(undefined ?? null).toBeNull();
expect(false ?? true).toBe(false);
expect(0 ?? 1).toBe(0);
expect("" ?? "foo").toBe("");
var obj = { exists: true };
expect(obj.exists ?? false).toBe(true);
expect(obj.doesNotExist ?? "foo").toBe("foo");
var counter = 0;
function sideEffect() { return counter++; }
expect(sideEffect() ?? -1).toBe(0);
var counter2 = 0;
var obj2 = {
get foo() { return counter2++; }
};
expect(obj2.foo ?? -1).toBe(0);

View File

@ -0,0 +1,5 @@
var _foo$bar;
var {
qux = (_foo$bar = foo.bar) != null ? _foo$bar : "qux"
} = {};

View File

@ -0,0 +1,3 @@
function foo(foo, qux = foo.bar ?? "qux") {}
function bar(bar, qux = bar ?? "qux") {}

View File

@ -0,0 +1,7 @@
function foo(foo, qux = (() => {
var _foo$bar;
return (_foo$bar = foo.bar) != null ? _foo$bar : "qux";
})()) {}
function bar(bar, qux = bar != null ? bar : "qux") {}

View File

@ -0,0 +1,3 @@
function foo(opts) {
var foo = opts.foo ?? "default";
}

View File

@ -0,0 +1,5 @@
function foo(opts) {
var _opts$foo;
var foo = (_opts$foo = opts.foo) != null ? _opts$foo : "default";
}

View File

@ -0,0 +1 @@
function foo(foo, bar = foo != null ? foo : "bar") {}

View File

@ -0,0 +1,3 @@
function foo() {
var foo = this != null ? this : {};
}

View File

@ -0,0 +1,3 @@
function foo(opts) {
var foo = opts.foo ?? "default";
}

View File

@ -0,0 +1,5 @@
function foo(opts) {
var _opts$foo;
var foo = (_opts$foo = opts.foo) != null ? _opts$foo : "default";
}

View File

@ -23,6 +23,11 @@ export default declare((api, opts) => {
throw new Error(".loose must be a boolean, or undefined");
}
const ignoreFunctionLength = api.assumption("ignoreFunctionLength") ?? loose;
const objectRestNoSymbols = api.assumption("objectRestNoSymbols") ?? loose;
const pureGetters = api.assumption("pureGetters") ?? loose;
const setSpreadProperties = api.assumption("setSpreadProperties") ?? loose;
function getExtendsHelper(file) {
return useBuiltIns
? t.memberExpression(t.identifier("Object"), t.identifier("assign"))
@ -133,7 +138,7 @@ export default declare((api, opts) => {
}
//expects path to an object pattern
function createObjectSpread(path, file, objRef) {
function createObjectRest(path, file, objRef) {
const props = path.get("properties");
const last = props[props.length - 1];
t.assertRestElement(last.node);
@ -172,7 +177,9 @@ export default declare((api, opts) => {
impureComputedPropertyDeclarators,
restElement.argument,
t.callExpression(
file.addHelper(`objectWithoutProperties${loose ? "Loose" : ""}`),
file.addHelper(
`objectWithoutProperties${objectRestNoSymbols ? "Loose" : ""}`,
),
[t.cloneNode(objRef), keyExpression],
),
];
@ -275,7 +282,7 @@ export default declare((api, opts) => {
idx >= i - 1 || paramsWithRestElement.has(idx);
convertFunctionParams(
path,
loose,
ignoreFunctionLength,
shouldTransformParam,
replaceRestElement,
);
@ -361,9 +368,9 @@ export default declare((api, opts) => {
impureComputedPropertyDeclarators,
argument,
callExpression,
] = createObjectSpread(objectPatternPath, file, ref);
] = createObjectRest(objectPatternPath, file, ref);
if (loose) {
if (pureGetters) {
removeUnusedExcludedKeys(objectPatternPath);
}
@ -444,7 +451,7 @@ export default declare((api, opts) => {
impureComputedPropertyDeclarators,
argument,
callExpression,
] = createObjectSpread(leftPath, file, t.identifier(refName));
] = createObjectRest(leftPath, file, t.identifier(refName));
if (impureComputedPropertyDeclarators.length > 0) {
nodes.push(
@ -553,7 +560,7 @@ export default declare((api, opts) => {
if (!hasSpread(path.node)) return;
let helper;
if (loose) {
if (setSpreadProperties) {
helper = getExtendsHelper(file);
} else {
try {
@ -583,10 +590,9 @@ export default declare((api, opts) => {
return;
}
// In loose mode, we don't want to make multiple calls. We're assuming
// that the spread objects either don't use getters, or that the
// getters are pure and don't depend on the order of evaluation.
if (loose) {
// When we can assume that getters are pure and don't depend on
// the order of evaluation, we can avoid making multiple calls.
if (pureGetters) {
if (hadProps) {
exp.arguments.push(obj);
}

View File

@ -0,0 +1,8 @@
{
"plugins": [
"proposal-object-rest-spread"
],
"assumptions": {
"ignoreFunctionLength": true
}
}

View File

@ -0,0 +1,3 @@
({...R}, a = R) => {}
({...R}, e, c = 2, a = R, f = q) => { let q; }
({...R}, a = f(R)) => {}

View File

@ -0,0 +1,33 @@
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
(_ref, a) => {
let R = _extends({}, _ref);
if (a === void 0) {
a = R;
}
};
(_ref2, e, c = 2, a, f) => {
let R = _extends({}, _ref2);
if (a === void 0) {
a = R;
}
if (f === void 0) {
f = q;
}
return function () {
let q;
}();
};
(_ref3, a) => {
let R = _extends({}, _ref3);
if (a === void 0) {
a = f(R);
}
};

View File

@ -0,0 +1,9 @@
{
"plugins": [
["external-helpers", { "helperVersion": "7.100.0"}],
"proposal-object-rest-spread"
],
"assumptions": {
"objectRestNoSymbols": true
}
}

View File

@ -0,0 +1,7 @@
var _obj = obj;
({
a,
b
} = _obj);
c = babelHelpers.objectWithoutPropertiesLoose(_obj, ["a", "b"]);
_obj;

View File

@ -0,0 +1,5 @@
let _a = a,
{
[_a]: b
} = obj,
c = babelHelpers.objectWithoutPropertiesLoose(obj, [_a].map(babelHelpers.toPropertyKey));

View File

@ -0,0 +1,7 @@
let sym = Symbol();
let { a, ...r } = { a: 1, b: 2, [sym]: 3 };
expect(a).toBe(1);
expect(r.b).toBe(2);
expect(sym in r).toBe(false);

View File

@ -0,0 +1 @@
let { a, nested: { b, c, ...d }, e } = obj;

View File

@ -0,0 +1,9 @@
let {
a,
nested: {
b,
c
},
e
} = obj,
d = babelHelpers.objectWithoutPropertiesLoose(obj.nested, ["b", "c"]);

View File

@ -0,0 +1,5 @@
var {
a,
b
} = obj,
c = babelHelpers.objectWithoutPropertiesLoose(obj, ["a", "b"]);

Some files were not shown because too many files have changed in this diff Show More