"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = _default; var _core = require("@babel/core"); var _pluginSyntaxDecorators = require("@babel/plugin-syntax-decorators"); var _helperReplaceSupers = require("@babel/helper-replace-supers"); var _helperSplitExportDeclaration = require("@babel/helper-split-export-declaration"); function incrementId(id, idx = id.length - 1) { if (idx === -1) { id.unshift(65); return; } const current = id[idx]; if (current === 90) { id[idx] = 97; } else if (current === 122) { id[idx] = 65; incrementId(id, idx - 1); } else { id[idx] = current + 1; } } function createPrivateUidGeneratorForClass(classPath) { const currentPrivateId = []; const privateNames = new Set(); classPath.traverse({ PrivateName(path) { privateNames.add(path.node.id.name); } }); return () => { let reifiedId; do { incrementId(currentPrivateId); reifiedId = String.fromCharCode(...currentPrivateId); } while (privateNames.has(reifiedId)); return _core.types.privateName(_core.types.identifier(reifiedId)); }; } function createLazyPrivateUidGeneratorForClass(classPath) { let generator; return () => { if (!generator) { generator = createPrivateUidGeneratorForClass(classPath); } return generator(); }; } function replaceClassWithVar(path) { if (path.type === "ClassDeclaration") { const varId = path.scope.generateUidIdentifierBasedOnNode(path.node.id); const classId = _core.types.identifier(path.node.id.name); path.scope.rename(classId.name, varId.name); path.insertBefore(_core.types.variableDeclaration("let", [_core.types.variableDeclarator(varId)])); path.get("id").replaceWith(classId); return [_core.types.cloneNode(varId), path]; } else { let className; let varId; if (path.node.id) { className = path.node.id.name; varId = path.scope.parent.generateDeclaredUidIdentifier(className); path.scope.rename(className, varId.name); } else if (path.parentPath.node.type === "VariableDeclarator" && path.parentPath.node.id.type === "Identifier") { className = path.parentPath.node.id.name; varId = path.scope.parent.generateDeclaredUidIdentifier(className); } else { varId = path.scope.parent.generateDeclaredUidIdentifier("decorated_class"); } const newClassExpr = _core.types.classExpression(className && _core.types.identifier(className), path.node.superClass, path.node.body); const [newPath] = path.replaceWith(_core.types.sequenceExpression([newClassExpr, varId])); return [_core.types.cloneNode(varId), newPath.get("expressions.0")]; } } function generateClassProperty(key, value, isStatic) { if (key.type === "PrivateName") { return _core.types.classPrivateProperty(key, value, undefined, isStatic); } else { return _core.types.classProperty(key, value, undefined, undefined, isStatic); } } function addProxyAccessorsFor(element, originalKey, targetKey, isComputed = false) { const { static: isStatic } = element.node; const getterBody = _core.types.blockStatement([_core.types.returnStatement(_core.types.memberExpression(_core.types.thisExpression(), _core.types.cloneNode(targetKey)))]); const setterBody = _core.types.blockStatement([_core.types.expressionStatement(_core.types.assignmentExpression("=", _core.types.memberExpression(_core.types.thisExpression(), _core.types.cloneNode(targetKey)), _core.types.identifier("v")))]); let getter, setter; if (originalKey.type === "PrivateName") { getter = _core.types.classPrivateMethod("get", _core.types.cloneNode(originalKey), [], getterBody, isStatic); setter = _core.types.classPrivateMethod("set", _core.types.cloneNode(originalKey), [_core.types.identifier("v")], setterBody, isStatic); } else { getter = _core.types.classMethod("get", _core.types.cloneNode(originalKey), [], getterBody, isComputed, isStatic); setter = _core.types.classMethod("set", _core.types.cloneNode(originalKey), [_core.types.identifier("v")], setterBody, isComputed, isStatic); } element.insertAfter(setter); element.insertAfter(getter); } function extractProxyAccessorsFor(targetKey) { return [_core.types.functionExpression(undefined, [], _core.types.blockStatement([_core.types.returnStatement(_core.types.memberExpression(_core.types.thisExpression(), _core.types.cloneNode(targetKey)))])), _core.types.functionExpression(undefined, [_core.types.identifier("value")], _core.types.blockStatement([_core.types.expressionStatement(_core.types.assignmentExpression("=", _core.types.memberExpression(_core.types.thisExpression(), _core.types.cloneNode(targetKey)), _core.types.identifier("value")))]))]; } const FIELD = 0; const ACCESSOR = 1; const METHOD = 2; const GETTER = 3; const SETTER = 4; const STATIC = 5; function getElementKind(element) { switch (element.node.type) { case "ClassProperty": case "ClassPrivateProperty": return FIELD; case "ClassAccessorProperty": return ACCESSOR; case "ClassMethod": case "ClassPrivateMethod": if (element.node.kind === "get") { return GETTER; } else if (element.node.kind === "set") { return SETTER; } else { return METHOD; } } } function isDecoratorInfo(info) { return "decorators" in info; } function filteredOrderedDecoratorInfo(info) { 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) { return _core.types.arrayExpression(filteredOrderedDecoratorInfo(info).map(el => { const decs = el.decorators.length > 1 ? _core.types.arrayExpression(el.decorators) : el.decorators[0]; const kind = el.isStatic ? el.kind + STATIC : el.kind; const decInfo = [decs, _core.types.numericLiteral(kind), el.name]; const { privateMethods } = el; if (Array.isArray(privateMethods)) { decInfo.push(...privateMethods); } else if (privateMethods) { decInfo.push(privateMethods); } return _core.types.arrayExpression(decInfo); })); } function extractElementLocalAssignments(decorationInfo) { const localIds = []; for (const el of filteredOrderedDecoratorInfo(decorationInfo)) { const { locals } = el; if (Array.isArray(locals)) { localIds.push(...locals); } else if (locals !== undefined) { localIds.push(locals); } } return localIds; } function addCallAccessorsFor(element, key, getId, setId) { element.insertAfter(_core.types.classPrivateMethod("get", _core.types.cloneNode(key), [], _core.types.blockStatement([_core.types.returnStatement(_core.types.callExpression(_core.types.cloneNode(getId), [_core.types.thisExpression()]))]))); element.insertAfter(_core.types.classPrivateMethod("set", _core.types.cloneNode(key), [_core.types.identifier("v")], _core.types.blockStatement([_core.types.expressionStatement(_core.types.callExpression(_core.types.cloneNode(setId), [_core.types.thisExpression(), _core.types.identifier("v")]))]))); } function isNotTsParameter(node) { return node.type !== "TSParameterProperty"; } function movePrivateAccessor(element, key, methodLocalVar, isStatic) { let params; let block; if (element.node.kind === "set") { params = [_core.types.identifier("v")]; block = [_core.types.expressionStatement(_core.types.callExpression(methodLocalVar, [_core.types.thisExpression(), _core.types.identifier("v")]))]; } else { params = []; block = [_core.types.returnStatement(_core.types.callExpression(methodLocalVar, [_core.types.thisExpression()]))]; } element.replaceWith(_core.types.classPrivateMethod(element.node.kind, _core.types.cloneNode(key), params, _core.types.blockStatement(block), isStatic)); } function isClassDecoratableElementPath(path) { const { type } = path; return type !== "TSDeclareMethod" && type !== "TSIndexSignature" && type !== "StaticBlock"; } function staticBlockToIIFE(block) { return _core.types.callExpression(_core.types.arrowFunctionExpression([], _core.types.blockStatement(block.body)), []); } function maybeSequenceExpression(exprs) { if (exprs.length === 0) return _core.types.unaryExpression("void", _core.types.numericLiteral(0)); if (exprs.length === 1) return exprs[0]; return _core.types.sequenceExpression(exprs); } function transformClass(path, state, constantSuper) { const body = path.get("body.body"); const classDecorators = path.node.decorators; let hasElementDecorators = false; const generateClassPrivateUid = createLazyPrivateUidGeneratorForClass(path); for (const element of body) { if (!isClassDecoratableElementPath(element)) { continue; } if (element.node.decorators && element.node.decorators.length > 0) { hasElementDecorators = true; } else if (element.node.type === "ClassAccessorProperty") { const { key, value, static: isStatic } = element.node; const newId = generateClassPrivateUid(); const valueNode = value ? _core.types.cloneNode(value) : undefined; const newField = generateClassProperty(newId, valueNode, isStatic); const [newPath] = element.replaceWith(newField); addProxyAccessorsFor(newPath, key, newId, element.node.computed); } } if (!classDecorators && !hasElementDecorators) return; const elementDecoratorInfo = []; let firstFieldPath; let constructorPath; let requiresProtoInit = false; let requiresStaticInit = false; const decoratedPrivateMethods = new Set(); let protoInitLocal, staticInitLocal, classInitLocal, classLocal; const assignments = []; const scopeParent = path.scope.parent; const memoiseExpression = (expression, hint) => { const localEvaluatedId = scopeParent.generateDeclaredUidIdentifier(hint); assignments.push(_core.types.assignmentExpression("=", localEvaluatedId, expression)); return _core.types.cloneNode(localEvaluatedId); }; if (classDecorators) { classInitLocal = scopeParent.generateDeclaredUidIdentifier("initClass"); const [localId, classPath] = replaceClassWithVar(path); path = classPath; classLocal = localId; path.node.decorators = null; for (const classDecorator of classDecorators) { if (!scopeParent.isStatic(classDecorator.expression)) { classDecorator.expression = memoiseExpression(classDecorator.expression, "dec"); } } } else { if (!path.node.id) { path.node.id = path.scope.generateUidIdentifier("Class"); } classLocal = _core.types.cloneNode(path.node.id); } if (hasElementDecorators) { for (const element of body) { if (!isClassDecoratableElementPath(element)) { continue; } const { node } = element; const decorators = element.get("decorators"); const hasDecorators = Array.isArray(decorators) && decorators.length > 0; if (hasDecorators) { for (const decoratorPath of decorators) { if (!scopeParent.isStatic(decoratorPath.node.expression)) { decoratorPath.node.expression = memoiseExpression(decoratorPath.node.expression, "dec"); } } } const isComputed = "computed" in element.node && element.node.computed === true; if (isComputed) { if (!scopeParent.isStatic(node.key)) { node.key = memoiseExpression(node.key, "computedKey"); } } const kind = getElementKind(element); const { key } = node; const isPrivate = key.type === "PrivateName"; const isStatic = !!element.node.static; let name = "computedKey"; if (isPrivate) { name = key.id.name; } else if (!isComputed && key.type === "Identifier") { name = key.name; } if (element.isClassMethod({ kind: "constructor" })) { constructorPath = element; } if (hasDecorators) { let locals; let privateMethods; if (kind === ACCESSOR) { const { value } = element.node; const params = [_core.types.thisExpression()]; if (value) { params.push(_core.types.cloneNode(value)); } const newId = generateClassPrivateUid(); const newFieldInitId = element.scope.parent.generateDeclaredUidIdentifier(`init_${name}`); const newValue = _core.types.callExpression(_core.types.cloneNode(newFieldInitId), params); const newField = generateClassProperty(newId, newValue, isStatic); const [newPath] = element.replaceWith(newField); if (isPrivate) { privateMethods = extractProxyAccessorsFor(newId); const getId = newPath.scope.parent.generateDeclaredUidIdentifier(`get_${name}`); const setId = newPath.scope.parent.generateDeclaredUidIdentifier(`set_${name}`); addCallAccessorsFor(newPath, key, getId, setId); locals = [newFieldInitId, getId, setId]; } else { addProxyAccessorsFor(newPath, key, newId, isComputed); locals = newFieldInitId; } } else if (kind === FIELD) { const initId = element.scope.parent.generateDeclaredUidIdentifier(`init_${name}`); const valuePath = element.get("value"); valuePath.replaceWith(_core.types.callExpression(_core.types.cloneNode(initId), [_core.types.thisExpression(), valuePath.node].filter(v => v))); locals = initId; if (isPrivate) { privateMethods = extractProxyAccessorsFor(key); } } else if (isPrivate) { locals = element.scope.parent.generateDeclaredUidIdentifier(`call_${name}`); const replaceSupers = new _helperReplaceSupers.default({ constantSuper, methodPath: element, objectRef: classLocal, superRef: path.node.superClass, file: state, refToPreserve: classLocal }); replaceSupers.replace(); const { params, body, async: isAsync } = element.node; privateMethods = _core.types.functionExpression(undefined, params.filter(isNotTsParameter), body, isAsync); if (kind === GETTER || kind === SETTER) { movePrivateAccessor(element, _core.types.cloneNode(key), _core.types.cloneNode(locals), isStatic); } else { const node = element.node; path.node.body.body.unshift(_core.types.classPrivateProperty(key, _core.types.cloneNode(locals), [], node.static)); decoratedPrivateMethods.add(key.id.name); element.remove(); } } let nameExpr; if (isComputed) { nameExpr = _core.types.cloneNode(key); } else if (key.type === "PrivateName") { nameExpr = _core.types.stringLiteral(key.id.name); } else if (key.type === "Identifier") { nameExpr = _core.types.stringLiteral(key.name); } else { nameExpr = _core.types.cloneNode(key); } elementDecoratorInfo.push({ kind, decorators: decorators.map(d => d.node.expression), name: nameExpr, isStatic, privateMethods, locals }); if (kind !== FIELD) { if (isStatic) { requiresStaticInit = true; } else { requiresProtoInit = true; } } if (element.node) { element.node.decorators = null; } if (!firstFieldPath && (kind === FIELD || kind === ACCESSOR)) { firstFieldPath = element; } } } } const elementDecorations = generateDecorationExprs(elementDecoratorInfo); const classDecorations = _core.types.arrayExpression((classDecorators || []).map(d => d.expression)); const locals = extractElementLocalAssignments(elementDecoratorInfo); if (requiresProtoInit) { protoInitLocal = scopeParent.generateDeclaredUidIdentifier("initProto"); locals.push(protoInitLocal); const protoInitCall = _core.types.callExpression(_core.types.cloneNode(protoInitLocal), [_core.types.thisExpression()]); if (firstFieldPath) { const value = firstFieldPath.get("value"); const body = [protoInitCall]; if (value.node) { body.push(value.node); } value.replaceWith(_core.types.sequenceExpression(body)); } else if (constructorPath) { if (path.node.superClass) { path.traverse({ CallExpression: { exit(path) { if (!path.get("callee").isSuper()) return; path.replaceWith(_core.types.callExpression(_core.types.cloneNode(protoInitLocal), [path.node])); path.skip(); } } }); } else { constructorPath.node.body.body.unshift(_core.types.expressionStatement(protoInitCall)); } } else { const body = [_core.types.expressionStatement(protoInitCall)]; if (path.node.superClass) { body.unshift(_core.types.expressionStatement(_core.types.callExpression(_core.types.super(), [_core.types.spreadElement(_core.types.identifier("args"))]))); } path.node.body.body.unshift(_core.types.classMethod("constructor", _core.types.identifier("constructor"), [_core.types.restElement(_core.types.identifier("args"))], _core.types.blockStatement(body))); } } if (requiresStaticInit) { staticInitLocal = scopeParent.generateDeclaredUidIdentifier("initStatic"); locals.push(staticInitLocal); } if (decoratedPrivateMethods.size > 0) { path.traverse({ PrivateName(path) { if (!decoratedPrivateMethods.has(path.node.id.name)) return; const parentPath = path.parentPath; const parentParentPath = parentPath.parentPath; if (parentParentPath.node.type === "AssignmentExpression" && parentParentPath.node.left === parentPath.node || parentParentPath.node.type === "UpdateExpression" || parentParentPath.node.type === "RestElement" || parentParentPath.node.type === "ArrayPattern" || parentParentPath.node.type === "ObjectProperty" && parentParentPath.node.value === parentPath.node && parentParentPath.parentPath.type === "ObjectPattern" || parentParentPath.node.type === "ForOfStatement" && parentParentPath.node.left === parentPath.node) { throw path.buildCodeFrameError(`Decorated private methods are not updatable, but "#${path.node.id.name}" is updated via this expression.`); } } }); } let classInitInjected = false; const classInitCall = classInitLocal && _core.types.callExpression(_core.types.cloneNode(classInitLocal), []); const originalClass = path.node; if (classDecorators) { locals.push(classLocal, classInitLocal); const statics = []; let staticBlocks = []; path.get("body.body").forEach(element => { if (element.isStaticBlock()) { staticBlocks.push(element.node); element.remove(); return; } const isProperty = element.isClassProperty() || element.isClassPrivateProperty(); if ((isProperty || element.isClassPrivateMethod()) && element.node.static) { if (isProperty && staticBlocks.length > 0) { const allValues = staticBlocks.map(staticBlockToIIFE); if (element.node.value) allValues.push(element.node.value); element.node.value = maybeSequenceExpression(allValues); staticBlocks = []; } element.node.static = false; statics.push(element.node); element.remove(); } }); if (statics.length > 0 || staticBlocks.length > 0) { const staticsClass = _core.template.expression.ast` class extends ${state.addHelper("identity")} {} `; staticsClass.body.body = [_core.types.staticBlock([_core.types.toStatement(path.node, false)]), ...statics]; const constructorBody = []; const newExpr = _core.types.newExpression(staticsClass, []); if (staticBlocks.length > 0) { constructorBody.push(...staticBlocks.map(staticBlockToIIFE)); } if (classInitCall) { classInitInjected = true; constructorBody.push(classInitCall); } if (constructorBody.length > 0) { constructorBody.unshift(_core.types.callExpression(_core.types.super(), [_core.types.cloneNode(classLocal)])); staticsClass.body.body.push(_core.types.classMethod("constructor", _core.types.identifier("constructor"), [], _core.types.blockStatement([_core.types.expressionStatement(_core.types.sequenceExpression(constructorBody))]))); } else { newExpr.arguments.push(_core.types.cloneNode(classLocal)); } path.replaceWith(newExpr); } } if (!classInitInjected && classInitCall) { path.node.body.body.push(_core.types.staticBlock([_core.types.expressionStatement(classInitCall)])); } originalClass.body.body.unshift(_core.types.staticBlock([_core.types.expressionStatement(_core.types.assignmentExpression("=", _core.types.arrayPattern(locals), _core.types.callExpression(state.addHelper("applyDecs"), [_core.types.thisExpression(), elementDecorations, classDecorations]))), requiresStaticInit && _core.types.expressionStatement(_core.types.callExpression(_core.types.cloneNode(staticInitLocal), [_core.types.thisExpression()]))].filter(Boolean))); path.insertBefore(assignments.map(expr => _core.types.expressionStatement(expr))); path.scope.crawl(); return path; } function _default({ assertVersion, assumption }, { loose }) { var _assumption; assertVersion("^7.16.0"); const VISITED = new WeakSet(); const constantSuper = (_assumption = assumption("constantSuper")) != null ? _assumption : loose; return { name: "proposal-decorators", inherits: _pluginSyntaxDecorators.default, visitor: { "ExportNamedDeclaration|ExportDefaultDeclaration"(path) { var _declaration$decorato; const { declaration } = path.node; if ((declaration == null ? void 0 : declaration.type) === "ClassDeclaration" && ((_declaration$decorato = declaration.decorators) == null ? void 0 : _declaration$decorato.length) > 0) { (0, _helperSplitExportDeclaration.default)(path); } }, Class(path, state) { if (VISITED.has(path)) return; const newPath = transformClass(path, state, constantSuper); if (newPath) VISITED.add(newPath); } } }; }