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

View File

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

View File

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

View File

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

View File

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

View File

@ -255,7 +255,7 @@ const looseHandlers = {
type ReplaceSupersOptionsBase = {| type ReplaceSupersOptionsBase = {|
methodPath: NodePath, methodPath: NodePath,
superRef: Object, superRef: Object,
isLoose: boolean, constantSuper: boolean,
file: any, file: any,
// objectRef might have been shadowed in child scopes, // objectRef might have been shadowed in child scopes,
// in that case, we need to rename related variables. // in that case, we need to rename related variables.
@ -284,13 +284,16 @@ export default class ReplaceSupers {
this.file = opts.file; this.file = opts.file;
this.superRef = opts.superRef; 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; this.opts = opts;
} }
declare file: HubInterface; declare file: HubInterface;
declare isDerivedConstructor: boolean; declare isDerivedConstructor: boolean;
declare isLoose: boolean; declare constantSuper: boolean;
declare isPrivateMethod: boolean; declare isPrivateMethod: boolean;
declare isStatic: boolean; declare isStatic: boolean;
declare methodPath: NodePath; 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, { memberExpressionToFunctions(this.methodPath, visitor, {
file: this.file, file: this.file,

View File

@ -57,7 +57,7 @@ function classOrObjectMethod(path: NodePath, callId: Object) {
.unwrapFunctionEnvironment(); .unwrapFunctionEnvironment();
} }
function plainFunction(path: NodePath, callId: Object) { function plainFunction(path: NodePath, callId: Object, noNewArrows: boolean) {
const node = path.node; const node = path.node;
const isDeclaration = path.isFunctionDeclaration(); const isDeclaration = path.isFunctionDeclaration();
const functionId = node.id; const functionId = node.id;
@ -68,7 +68,7 @@ function plainFunction(path: NodePath, callId: Object) {
: buildAnonymousExpressionWrapper; : buildAnonymousExpressionWrapper;
if (path.isArrowFunctionExpression()) { if (path.isArrowFunctionExpression()) {
path.arrowFunctionToExpression(); path.arrowFunctionToExpression({ noNewArrows });
} }
node.id = null; 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()) { if (path.isMethod()) {
classOrObjectMethod(path, callId); classOrObjectMethod(path, callId);
} else { } else {
plainFunction(path, callId); plainFunction(path, callId, noNewArrows);
} }
} }

View File

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

View File

@ -12,6 +12,7 @@ export default declare((api, options) => {
return createClassFeaturePlugin({ return createClassFeaturePlugin({
name: "proposal-class-properties", name: "proposal-class-properties",
api,
feature: FEATURES.fields, feature: FEATURES.fields,
loose: options.loose, 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({ return createClassFeaturePlugin({
name: "proposal-decorators", name: "proposal-decorators",
api,
feature: FEATURES.decorators, feature: FEATURES.decorators,
// loose: options.loose, Not supported // 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 }) => { export default declare((api, { loose = false }) => {
api.assertVersion(7); api.assertVersion(7);
const noDocumentAll = api.assumption("noDocumentAll") ?? loose;
return { return {
name: "proposal-nullish-coalescing-operator", name: "proposal-nullish-coalescing-operator",
@ -38,7 +39,7 @@ export default declare((api, { loose = false }) => {
t.conditionalExpression( t.conditionalExpression(
// We cannot use `!= null` in spec mode because // We cannot use `!= null` in spec mode because
// `document.all == null` and `document.all` is not "nullish". // `document.all == null` and `document.all` is not "nullish".
loose noDocumentAll
? t.binaryExpression("!=", assignment, t.nullLiteral()) ? t.binaryExpression("!=", assignment, t.nullLiteral())
: t.logicalExpression( : 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"); 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) { function getExtendsHelper(file) {
return useBuiltIns return useBuiltIns
? t.memberExpression(t.identifier("Object"), t.identifier("assign")) ? t.memberExpression(t.identifier("Object"), t.identifier("assign"))
@ -133,7 +138,7 @@ export default declare((api, opts) => {
} }
//expects path to an object pattern //expects path to an object pattern
function createObjectSpread(path, file, objRef) { function createObjectRest(path, file, objRef) {
const props = path.get("properties"); const props = path.get("properties");
const last = props[props.length - 1]; const last = props[props.length - 1];
t.assertRestElement(last.node); t.assertRestElement(last.node);
@ -172,7 +177,9 @@ export default declare((api, opts) => {
impureComputedPropertyDeclarators, impureComputedPropertyDeclarators,
restElement.argument, restElement.argument,
t.callExpression( t.callExpression(
file.addHelper(`objectWithoutProperties${loose ? "Loose" : ""}`), file.addHelper(
`objectWithoutProperties${objectRestNoSymbols ? "Loose" : ""}`,
),
[t.cloneNode(objRef), keyExpression], [t.cloneNode(objRef), keyExpression],
), ),
]; ];
@ -275,7 +282,7 @@ export default declare((api, opts) => {
idx >= i - 1 || paramsWithRestElement.has(idx); idx >= i - 1 || paramsWithRestElement.has(idx);
convertFunctionParams( convertFunctionParams(
path, path,
loose, ignoreFunctionLength,
shouldTransformParam, shouldTransformParam,
replaceRestElement, replaceRestElement,
); );
@ -361,9 +368,9 @@ export default declare((api, opts) => {
impureComputedPropertyDeclarators, impureComputedPropertyDeclarators,
argument, argument,
callExpression, callExpression,
] = createObjectSpread(objectPatternPath, file, ref); ] = createObjectRest(objectPatternPath, file, ref);
if (loose) { if (pureGetters) {
removeUnusedExcludedKeys(objectPatternPath); removeUnusedExcludedKeys(objectPatternPath);
} }
@ -444,7 +451,7 @@ export default declare((api, opts) => {
impureComputedPropertyDeclarators, impureComputedPropertyDeclarators,
argument, argument,
callExpression, callExpression,
] = createObjectSpread(leftPath, file, t.identifier(refName)); ] = createObjectRest(leftPath, file, t.identifier(refName));
if (impureComputedPropertyDeclarators.length > 0) { if (impureComputedPropertyDeclarators.length > 0) {
nodes.push( nodes.push(
@ -553,7 +560,7 @@ export default declare((api, opts) => {
if (!hasSpread(path.node)) return; if (!hasSpread(path.node)) return;
let helper; let helper;
if (loose) { if (setSpreadProperties) {
helper = getExtendsHelper(file); helper = getExtendsHelper(file);
} else { } else {
try { try {
@ -583,10 +590,9 @@ export default declare((api, opts) => {
return; return;
} }
// In loose mode, we don't want to make multiple calls. We're assuming // When we can assume that getters are pure and don't depend on
// that the spread objects either don't use getters, or that the // the order of evaluation, we can avoid making multiple calls.
// getters are pure and don't depend on the order of evaluation. if (pureGetters) {
if (loose) {
if (hadProps) { if (hadProps) {
exp.arguments.push(obj); 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