UNPKG

@jsenv/plugin-transpilation

Version:
532 lines (454 loc) 13.1 kB
/* @minVersion 7.21.0 */ /* @mangleFns */ import checkInRHS from "../checkInRHS/checkInRHS.js"; import setFunctionName from "../setFunctionName/setFunctionName.js"; import toPropertyKey from "../toPropertyKey/toPropertyKey.js"; /** Basic usage: applyDecs( Class, [ // member decorators [ decs, // dec, or array of decs, or array of this values and decs 0, // kind of value being decorated 'prop', // name of public prop on class containing the value being decorated, '#p', // the name of the private property (if is private, void 0 otherwise), ] ], [ // class decorators dec1, dec2 ] ) ``` Fully transpiled example: ```js @dec class Class { @dec a = 123; @dec #a = 123; @dec @dec2 accessor b = 123; @dec accessor #b = 123; @dec c() { console.log('c'); } @dec #c() { console.log('privC'); } @dec get d() { console.log('d'); } @dec get #d() { console.log('privD'); } @dec set e(v) { console.log('e'); } @dec set #e(v) { console.log('privE'); } } // becomes let initializeInstance; let initializeClass; let initA; let initPrivA; let initB; let initPrivB, getPrivB, setPrivB; let privC; let privD; let privE; let Class; class _Class { static { let ret = applyDecs( this, [ [dec, 0, 'a'], [dec, 0, 'a', (i) => i.#a, (i, v) => i.#a = v], [[dec, dec2], 1, 'b'], [dec, 1, 'b', (i) => i.#privBData, (i, v) => i.#privBData = v], [dec, 2, 'c'], [dec, 2, 'c', () => console.log('privC')], [dec, 3, 'd'], [dec, 3, 'd', () => console.log('privD')], [dec, 4, 'e'], [dec, 4, 'e', () => console.log('privE')], ], [ dec ] ); initA = ret[0]; initPrivA = ret[1]; initB = ret[2]; initPrivB = ret[3]; getPrivB = ret[4]; setPrivB = ret[5]; privC = ret[6]; privD = ret[7]; privE = ret[8]; initializeInstance = ret[9]; Class = ret[10] initializeClass = ret[11]; } a = (initializeInstance(this), initA(this, 123)); #a = initPrivA(this, 123); #bData = initB(this, 123); get b() { return this.#bData } set b(v) { this.#bData = v } #privBData = initPrivB(this, 123); get #b() { return getPrivB(this); } set #b(v) { setPrivB(this, v); } c() { console.log('c'); } #c(...args) { return privC(this, ...args) } get d() { console.log('d'); } get #d() { return privD(this); } set e(v) { console.log('e'); } set #e(v) { privE(this, v); } } initializeClass(Class); */ export default /* @no-mangle */ function applyDecs2305( targetClass, memberDecs, classDecs, classDecsHaveThis, instanceBrand, parentClass, ) { function _bindPropCall(obj, name, before) { return function (_this, value) { if (before) { before(_this); } return obj[name].call(_this, value); }; } function runInitializers(initializers, value) { for (var i = 0; i < initializers.length; i++) { initializers[i].call(value); } return value; } function assertCallable(fn, hint1, hint2, throwUndefined) { if (typeof fn !== "function") { if (throwUndefined || fn !== void 0) { throw new TypeError( hint1 + " must " + (hint2 || "be") + " a function" + (throwUndefined ? "" : " or undefined"), ); } } return fn; } /* @no-mangle */ function applyDec( Class, decInfo, decoratorsHaveThis, name, kind, metadata, initializers, ret, isStatic, isPrivate, isField, isAccessor, hasPrivateBrand, ) { function assertInstanceIfPrivate(target) { if (!hasPrivateBrand(target)) { throw new TypeError( "Attempted to access private element on non-instance", ); } } var decs = decInfo[0], decVal = decInfo[3], _, isClass = !ret; if (!isClass) { if (!decoratorsHaveThis && !Array.isArray(decs)) { decs = [decs]; } var desc = {}, init = [], key = kind === PROP_KIND.GETTER ? "get" : kind === PROP_KIND.SETTER || isAccessor ? "set" : "value"; if (isPrivate) { if (isField || isAccessor) { desc = { get: setFunctionName( function (_this) { return decVal(_this); }, name, "get", ), set: function (_this, value) { decInfo[4](_this, value); }, }; } else { desc[key] = decVal; } if (!isField) { setFunctionName( desc[key], name, kind === PROP_KIND.METHOD ? "" : key, ); } } else if (!isField) { desc = Object.getOwnPropertyDescriptor(Class, name); } } var newValue = Class; for (var i = decs.length - 1; i >= 0; i -= decoratorsHaveThis ? 2 : 1) { var dec = decs[i], decThis = decoratorsHaveThis ? decs[i - 1] : void 0; var decoratorFinishedRef = {}; var ctx = { kind: ["field", "accessor", "method", "getter", "setter", "class"][ kind ], name: name, metadata: metadata, addInitializer: function (decoratorFinishedRef, initializer) { if (decoratorFinishedRef.v) { throw new Error( "attempted to call addInitializer after decoration was finished", ); } assertCallable(initializer, "An initializer", "be", true); initializers.push(initializer); }.bind(null, decoratorFinishedRef), }; try { if (isClass) { if ( (_ = assertCallable( dec.call(decThis, newValue, ctx), "class decorators", "return", )) ) { newValue = _; } } else { ctx.static = isStatic; ctx.private = isPrivate; var get, set; if (!isPrivate) { get = function (target) { return target[name]; }; if (kind < PROP_KIND.METHOD || kind === PROP_KIND.SETTER) { set = function (target, v) { target[name] = v; }; } } else if (kind === PROP_KIND.METHOD) { get = function (_this) { assertInstanceIfPrivate(_this); return desc.value; }; } else { if (kind < PROP_KIND.SETTER) { get = _bindPropCall(desc, "get", assertInstanceIfPrivate); } if (kind !== PROP_KIND.GETTER) { set = _bindPropCall(desc, "set", assertInstanceIfPrivate); } } var access = (ctx.access = { has: isPrivate ? // @ts-expect-error no thisArg hasPrivateBrand.bind() : function (target) { return name in target; }, }); if (get) access.get = get; if (set) access.set = set; newValue = dec.call( decThis, isAccessor ? { get: desc.get, set: desc.set, } : desc[key], ctx, ); if (isAccessor) { if (typeof newValue === "object" && newValue) { if ((_ = assertCallable(newValue.get, "accessor.get"))) { desc.get = _; } if ((_ = assertCallable(newValue.set, "accessor.set"))) { desc.set = _; } if ((_ = assertCallable(newValue.init, "accessor.init"))) { init.push(_); } } else if (newValue !== void 0) { throw new TypeError( "accessor decorators must return an object with get, set, or init properties or void 0", ); } } else if ( assertCallable( newValue, (isField ? "field" : "method") + " decorators", "return", ) ) { if (isField) { init.push(newValue); } else { desc[key] = newValue; } } } } finally { decoratorFinishedRef.v = true; } } if (isField || isAccessor) { ret.push(function (instance, value) { for (var i = init.length - 1; i >= 0; i--) { value = init[i].call(instance, value); } return value; }); } if (!isField && !isClass) { if (isPrivate) { if (isAccessor) { ret.push(_bindPropCall(desc, "get"), _bindPropCall(desc, "set")); } else { ret.push( kind === PROP_KIND.METHOD ? desc[key] : _bindPropCall.call.bind(desc[key]), ); } } else { Object.defineProperty(Class, name, desc); } } return newValue; } /* @no-mangle */ function applyMemberDecs(Class, decInfos, instanceBrand, metadata) { var ret = []; var protoInitializers; var staticInitializers; var staticBrand = function (_) { return checkInRHS(_) === Class; }; var existingNonFields = new Map(); function pushInitializers(initializers) { if (initializers) { ret.push(runInitializers.bind(null, initializers)); } } for (var i = 0; i < decInfos.length; i++) { var decInfo = decInfos[i]; // skip computed property names if (!Array.isArray(decInfo)) continue; var kind = decInfo[1]; var name = decInfo[2]; var isPrivate = decInfo.length > 3; var decoratorsHaveThis = kind & PROP_KIND.DECORATORS_HAVE_THIS; var isStatic = !!(kind & PROP_KIND.STATIC); kind &= 7; /* 0b111 */ var isField = kind === PROP_KIND.FIELD; var key = name + "/" + isStatic; if (!isField && !isPrivate) { var existingKind = existingNonFields.get(key); if ( existingKind === true || (existingKind === PROP_KIND.GETTER && kind !== PROP_KIND.SETTER) || (existingKind === PROP_KIND.SETTER && kind !== PROP_KIND.GETTER) ) { throw new Error( "Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: " + name, ); } existingNonFields.set(key, kind > PROP_KIND.METHOD ? kind : true); } applyDec( isStatic ? Class : Class.prototype, decInfo, decoratorsHaveThis, isPrivate ? "#" + name : toPropertyKey(name), kind, metadata, isStatic ? (staticInitializers = staticInitializers || []) : (protoInitializers = protoInitializers || []), ret, isStatic, isPrivate, isField, kind === PROP_KIND.ACCESSOR, isStatic && isPrivate ? staticBrand : instanceBrand, ); } pushInitializers(protoInitializers); pushInitializers(staticInitializers); return ret; } function defineMetadata(Class, metadata) { return Object.defineProperty( Class, Symbol.metadata || Symbol.for("Symbol.metadata"), { configurable: true, enumerable: true, value: metadata }, ); } if (arguments.length >= 6) { var parentMetadata = parentClass[Symbol.metadata || Symbol.for("Symbol.metadata")]; } var metadata = Object.create(parentMetadata == null ? null : parentMetadata); var e = applyMemberDecs(targetClass, memberDecs, instanceBrand, metadata); if (!classDecs.length) defineMetadata(targetClass, metadata); return { e: e, // Lazily apply class decorations so that member init locals can be properly bound. get c() { // The transformer will not emit assignment when there are no class decorators, // so we don't have to return an empty array here. var initializers = []; return ( classDecs.length && [ defineMetadata( applyDec( targetClass, [classDecs], classDecsHaveThis, targetClass.name, PROP_KIND.CLASS, metadata, initializers, ), metadata, ), runInitializers.bind(null, initializers, targetClass), ] ); }, }; }