Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Update decorators to match latest spec #14353

Merged
2 changes: 1 addition & 1 deletion packages/babel-helpers/src/helpers-generated.ts

Large diffs are not rendered by default.

164 changes: 122 additions & 42 deletions packages/babel-helpers/src/helpers/applyDecs.js
Expand Up @@ -18,12 +18,16 @@
CLASS = 10; // only used in assertValidReturnValue
*/

function createMetadataMethodsForProperty(metadataMap, kind, property) {
function createMetadataMethodsForProperty(
metadataMap,
kind,
property,
decoratorFinishedRef
) {
return {
getMetadata: function (key) {
if (typeof key !== "symbol") {
throw new TypeError("Metadata keys must be symbols, received: " + key);
}
assertNotFinished(decoratorFinishedRef, "getMetadata");
assertMetadataKey(key);

var metadataForKey = metadataMap[key];

Expand All @@ -44,9 +48,8 @@ function createMetadataMethodsForProperty(metadataMap, kind, property) {
}
},
setMetadata: function (key, value) {
if (typeof key !== "symbol") {
throw new TypeError("Metadata keys must be symbols, received: " + key);
}
assertNotFinished(decoratorFinishedRef, "setMetadata");
assertMetadataKey(key);

var metadataForKey = metadataMap[key];

Expand Down Expand Up @@ -120,21 +123,24 @@ function convertMetadataMapToFinal(obj, metadataMap) {
obj[Symbol.metadata || Symbol.for("Symbol.metadata")] = metadataMap;
}

function createAddInitializerMethod(initializers) {
function createAddInitializerMethod(initializers, decoratorFinishedRef) {
return function addInitializer(initializer) {
assertNotFinished(decoratorFinishedRef, "addInitializer");
assertCallable(initializer, "An initializer");
initializers.push(initializer);
};
}

function memberDecCtx(
function memberDec(
dec,
name,
desc,
metadataMap,
initializers,
kind,
isStatic,
isPrivate
isPrivate,
value
) {
var kindStr;

Expand Down Expand Up @@ -162,8 +168,13 @@ function memberDecCtx(
isPrivate: isPrivate,
};

var decoratorFinishedRef = { v: false };

if (kind !== 0 /* FIELD */) {
ctx.addInitializer = createAddInitializerMethod(initializers);
ctx.addInitializer = createAddInitializerMethod(
initializers,
decoratorFinishedRef
);
}

var metadataKind, metadataName;
Expand Down Expand Up @@ -202,10 +213,36 @@ function memberDecCtx(
metadataName = name;
}

return Object.assign(
ctx,
createMetadataMethodsForProperty(metadataMap, metadataKind, metadataName)
);
try {
return dec(
value,
Object.assign(
ctx,
createMetadataMethodsForProperty(
metadataMap,
metadataKind,
metadataName,
decoratorFinishedRef
)
)
);
} finally {
decoratorFinishedRef.v = true;
}
}

function assertNotFinished(decoratorFinishedRef, fnName) {
if (decoratorFinishedRef.v) {
throw new Error(
"attempted to call " + fnName + " after decoration was finished"
);
}
}

function assertMetadataKey(key) {
if (typeof key !== "symbol") {
throw new TypeError("Metadata keys must be symbols, received: " + key);
}
}

function assertCallable(fn, hint) {
Expand All @@ -220,7 +257,7 @@ function assertValidReturnValue(kind, value) {
if (kind === 1 /* ACCESSOR */) {
if (type !== "object" || value === null) {
throw new TypeError(
"accessor decorators must return an object with get, set, or initializer properties or void 0"
"accessor decorators must return an object with get, set, or init properties or void 0"
);
}
if (value.get !== undefined) {
Expand All @@ -229,8 +266,11 @@ function assertValidReturnValue(kind, value) {
if (value.set !== undefined) {
assertCallable(value.set, "accessor.set");
}
if (value.initialize !== undefined) {
assertCallable(value.initialize, "accessor.initialize");
if (value.init !== undefined) {
assertCallable(value.init, "accessor.init");
}
if (value.initializer !== undefined) {
JLHwung marked this conversation as resolved.
Show resolved Hide resolved
assertCallable(value.initializer, "accessor.initializer");
}
} else if (type !== "function") {
var hint;
Expand Down Expand Up @@ -296,28 +336,36 @@ function applyMemberDec(
value = desc.set;
}

var ctx = memberDecCtx(
name,
desc,
metadataMap,
initializers,
kind,
isStatic,
isPrivate
);

var newValue, get, set;

if (typeof decs === "function") {
newValue = decs(value, ctx);
newValue = memberDec(
decs,
name,
desc,
metadataMap,
initializers,
kind,
isStatic,
isPrivate,
value
);

if (newValue !== void 0) {
assertValidReturnValue(kind, newValue);

if (kind === 0 /* FIELD */) {
initializer = newValue;
} else if (kind === 1 /* ACCESSOR */) {
initializer = newValue.initialize;
if (
(initializer = newValue.init) == null &&
(initializer = newValue.initializer) &&
typeof console !== "undefined"
) {
console.warn(
".initializer has been renamed to .init as of March 2022"
);
}

get = newValue.get || value.get;
set = newValue.set || value.set;
Expand All @@ -331,7 +379,17 @@ function applyMemberDec(
for (var i = decs.length - 1; i >= 0; i--) {
var dec = decs[i];

newValue = dec(value, ctx);
newValue = memberDec(
dec,
name,
desc,
metadataMap,
initializers,
kind,
isStatic,
isPrivate,
value
);

if (newValue !== void 0) {
assertValidReturnValue(kind, newValue);
Expand All @@ -340,7 +398,15 @@ function applyMemberDec(
if (kind === 0 /* FIELD */) {
newInit = newValue;
} else if (kind === 1 /* ACCESSOR */) {
newInit = newValue.initialize;
if (
(newInit = newValue.init) == null &&
(newInit = newValue.initializer) &&
typeof console !== "undefined"
) {
console.warn(
".initializer has been renamed to .init as of March 2022"
);
}

get = newValue.get || value.get;
set = newValue.set || value.set;
Expand Down Expand Up @@ -537,19 +603,33 @@ function applyClassDecs(ret, targetClass, metadataMap, classDecs) {
if (classDecs.length > 0) {
var initializers = [];
var newClass = targetClass;

var name = targetClass.name;
var ctx = Object.assign(
{
kind: "class",
name: name,
addInitializer: createAddInitializerMethod(initializers),
},
createMetadataMethodsForProperty(metadataMap, 0 /* CONSTRUCTOR */, name)
);

for (var i = classDecs.length - 1; i >= 0; i--) {
var nextNewClass = classDecs[i](newClass, ctx);
var decoratorFinishedRef = { v: false };

try {
var ctx = Object.assign(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not related to this PR: Object.assign is ES2015, we may need to import helpers.extends.

{
kind: "class",
name: name,
addInitializer: createAddInitializerMethod(
initializers,
decoratorFinishedRef
),
},
createMetadataMethodsForProperty(
metadataMap,
0 /* CONSTRUCTOR */,
name,
decoratorFinishedRef
)
);
var nextNewClass = classDecs[i](newClass, ctx);
} finally {
decoratorFinishedRef.v = true;
}

if (nextNewClass !== undefined) {
assertValidReturnValue(10 /* CLASS */, nextNewClass);
newClass = nextNewClass;
Expand Down
Expand Up @@ -311,11 +311,28 @@ function isDecoratorInfo(
return "decorators" in info;
}

function filteredOrderedDecoratorInfo(
info: (DecoratorInfo | ComputedPropInfo)[],
): DecoratorInfo[] {
const filtered = info.filter(isDecoratorInfo);

return [
...filtered.filter(
el => el.isStatic && el.kind >= ACCESSOR && el.kind <= SETTER,
),
...filtered.filter(
el => !el.isStatic && el.kind >= ACCESSOR && el.kind <= SETTER,
),
...filtered.filter(el => el.isStatic && el.kind === FIELD),
...filtered.filter(el => !el.isStatic && el.kind === FIELD),
];
}

function generateDecorationExprs(
info: (DecoratorInfo | ComputedPropInfo)[],
): t.ArrayExpression {
return t.arrayExpression(
info.filter(isDecoratorInfo).map(el => {
filteredOrderedDecoratorInfo(info).map(el => {
const decs =
el.decorators.length > 1
? t.arrayExpression(el.decorators)
Expand All @@ -341,19 +358,19 @@ function generateDecorationExprs(
function extractElementLocalAssignments(
decorationInfo: (DecoratorInfo | ComputedPropInfo)[],
) {
const locals: t.Identifier[] = [];
const localIds: t.Identifier[] = [];

for (const el of decorationInfo) {
if ("locals" in el && el.locals) {
if (Array.isArray(el.locals)) {
locals.push(...el.locals);
} else {
locals.push(el.locals);
}
for (const el of filteredOrderedDecoratorInfo(decorationInfo)) {
const { locals } = el;

if (Array.isArray(locals)) {
localIds.push(...locals);
} else if (locals !== undefined) {
localIds.push(locals);
}
}

return locals;
return localIds;
}

function addCallAccessorsFor(
Expand Down
Expand Up @@ -12,7 +12,7 @@ function dec({ get, set }, context) {
set.call(this, v + 1);
},

initialize(v) {
init(v) {
return v ? v : 1;
}
}
Expand Down
Expand Up @@ -12,7 +12,7 @@ function dec({ get, set }, context) {
set.call(this, v + 1);
},

initialize(v) {
init(v) {
return v ? v : 1;
}
}
Expand Down
Expand Up @@ -12,7 +12,7 @@ function dec({ get, set }, context) {
set.call(this, v + 1);
},

initialize(v) {
init(v) {
return v ? v : 1;
}
}
Expand Down
Expand Up @@ -12,7 +12,7 @@ function dec({ get, set }, context) {
set.call(this, v + 1);
},

initialize(v) {
init(v) {
return v ? v : 1;
}
}
Expand Down
Expand Up @@ -17,7 +17,7 @@ class Foo {
expect(elements).toHaveLength(2);

expect(elements[0].context.name).toBe("a");
expect(elements[0].val).toBe(undefined);
expect(elements[0].val()).toBe(1);

expect(elements[1].context.name).toBe("a");
expect(elements[1].val()).toBe(1);
expect(elements[1].val).toBe(undefined);
Expand Up @@ -13,4 +13,4 @@ class Foo {

}

[_init_a, _initProto] = babelHelpers.applyDecs(Foo, [[dec, 0, "a"], [dec, 2, "a"]], []);
[_init_a, _initProto] = babelHelpers.applyDecs(Foo, [[dec, 2, "a"], [dec, 0, "a"]], []);
Expand Up @@ -4,7 +4,7 @@ const dec = () => {};

class Foo {
static {
[_init_a, _initProto] = babelHelpers.applyDecs(this, [[dec, 0, "a"], [dec, 2, "a"]], []);
[_init_a, _initProto] = babelHelpers.applyDecs(this, [[dec, 2, "a"], [dec, 0, "a"]], []);
}
a = (_initProto(this), _init_a(this, 123));

Expand Down