UNPKG

@babel/plugin-proposal-decorators

Version:
185 lines (181 loc) 8.64 kB
import { declare } from '@babel/helper-plugin-utils'; import syntaxDecorators from '@babel/plugin-syntax-decorators'; import { createClassFeaturePlugin, FEATURES } from '@babel/helper-create-class-features-plugin'; import { template, types } from '@babel/core'; const buildClassDecorator = template.statement(` DECORATOR(CLASS_REF = INNER) || CLASS_REF; `); const buildClassPrototype = template(` CLASS_REF.prototype; `); const buildGetDescriptor = template(` Object.getOwnPropertyDescriptor(TARGET, PROPERTY); `); const buildGetObjectInitializer = template(` (TEMP = Object.getOwnPropertyDescriptor(TARGET, PROPERTY), (TEMP = TEMP ? TEMP.value : undefined), { enumerable: true, configurable: true, writable: true, initializer: function(){ return TEMP; } }) `); const WARNING_CALLS = new WeakSet(); function applyEnsureOrdering(path) { const decorators = (path.isClass() ? [path, ...path.get("body.body")] : path.get("properties")).reduce((acc, prop) => acc.concat(prop.node.decorators || []), []); const identDecorators = decorators.filter(decorator => !types.isIdentifier(decorator.expression)); if (identDecorators.length === 0) return; return types.sequenceExpression(identDecorators.map(decorator => { const expression = decorator.expression; const id = decorator.expression = path.scope.generateDeclaredUidIdentifier("dec"); return types.assignmentExpression("=", id, expression); }).concat([path.node])); } function applyClassDecorators(classPath) { if (!hasClassDecorators(classPath.node)) return; const decorators = classPath.node.decorators || []; classPath.node.decorators = null; const name = classPath.scope.generateDeclaredUidIdentifier("class"); return decorators.map(dec => dec.expression).reverse().reduce(function (acc, decorator) { return buildClassDecorator({ CLASS_REF: types.cloneNode(name), DECORATOR: types.cloneNode(decorator), INNER: acc }).expression; }, classPath.node); } function hasClassDecorators(classNode) { return !!classNode.decorators?.length; } function applyMethodDecorators(path, state) { if (!hasMethodDecorators(path.node.body.body)) return; return applyTargetDecorators(path, state, path.node.body.body); } function hasMethodDecorators(body) { return body.some(node => node.decorators?.length); } function applyObjectDecorators(path, state) { if (!hasMethodDecorators(path.node.properties)) return; return applyTargetDecorators(path, state, path.node.properties.filter(prop => prop.type !== "SpreadElement")); } function applyTargetDecorators(path, state, decoratedProps) { const name = path.scope.generateDeclaredUidIdentifier(path.isClass() ? "class" : "obj"); const exprs = decoratedProps.reduce(function (acc, node) { let decorators = []; if (node.decorators != null) { decorators = node.decorators; node.decorators = null; } if (decorators.length === 0) return acc; if (node.computed) { throw path.buildCodeFrameError("Computed method/property decorators are not yet supported."); } const property = types.isLiteral(node.key) ? node.key : types.stringLiteral(node.key.name); const target = path.isClass() && !node.static ? buildClassPrototype({ CLASS_REF: name }).expression : name; if (types.isClassProperty(node, { static: false })) { const descriptor = path.scope.generateDeclaredUidIdentifier("descriptor"); const initializer = node.value ? types.functionExpression(null, [], types.blockStatement([types.returnStatement(node.value)])) : types.nullLiteral(); node.value = types.callExpression(state.addHelper("initializerWarningHelper"), [descriptor, types.thisExpression()]); WARNING_CALLS.add(node.value); acc.push(types.assignmentExpression("=", types.cloneNode(descriptor), types.callExpression(state.addHelper("applyDecoratedDescriptor"), [types.cloneNode(target), types.cloneNode(property), types.arrayExpression(decorators.map(dec => types.cloneNode(dec.expression))), types.objectExpression([types.objectProperty(types.identifier("configurable"), types.booleanLiteral(true)), types.objectProperty(types.identifier("enumerable"), types.booleanLiteral(true)), types.objectProperty(types.identifier("writable"), types.booleanLiteral(true)), types.objectProperty(types.identifier("initializer"), initializer)])]))); } else { acc.push(types.callExpression(state.addHelper("applyDecoratedDescriptor"), [types.cloneNode(target), types.cloneNode(property), types.arrayExpression(decorators.map(dec => types.cloneNode(dec.expression))), types.isObjectProperty(node) || types.isClassProperty(node, { static: true }) ? buildGetObjectInitializer({ TEMP: path.scope.generateDeclaredUidIdentifier("init"), TARGET: types.cloneNode(target), PROPERTY: types.cloneNode(property) }).expression : buildGetDescriptor({ TARGET: types.cloneNode(target), PROPERTY: types.cloneNode(property) }).expression, types.cloneNode(target)])); } return acc; }, []); return types.sequenceExpression([types.assignmentExpression("=", types.cloneNode(name), path.node), types.sequenceExpression(exprs), types.cloneNode(name)]); } function decoratedClassToExpression({ node, scope }) { if (!hasClassDecorators(node) && !hasMethodDecorators(node.body.body)) { return; } const ref = node.id ? types.cloneNode(node.id) : scope.generateUidIdentifier("class"); return types.variableDeclaration("let", [types.variableDeclarator(ref, types.toExpression(node))]); } const visitor = { ExportDefaultDeclaration(path) { const decl = path.get("declaration"); if (!decl.isClassDeclaration()) return; const replacement = decoratedClassToExpression(decl); if (replacement) { const [varDeclPath] = path.replaceWithMultiple([replacement, types.exportNamedDeclaration(null, [types.exportSpecifier(types.cloneNode(replacement.declarations[0].id), types.identifier("default"))])]); if (!decl.node.id) { path.scope.registerDeclaration(varDeclPath); } } }, ClassDeclaration(path) { const replacement = decoratedClassToExpression(path); if (replacement) { const [newPath] = path.replaceWith(replacement); const decl = newPath.get("declarations.0"); const id = decl.node.id; const binding = path.scope.getOwnBinding(id.name); binding.identifier = id; binding.path = decl; } }, ClassExpression(path, state) { const decoratedClass = applyEnsureOrdering(path) || applyClassDecorators(path) || applyMethodDecorators(path, state); if (decoratedClass) path.replaceWith(decoratedClass); }, ObjectExpression(path, state) { const decoratedObject = applyEnsureOrdering(path) || applyObjectDecorators(path, state); if (decoratedObject) path.replaceWith(decoratedObject); }, AssignmentExpression(path, state) { if (!WARNING_CALLS.has(path.node.right)) return; path.replaceWith(types.callExpression(state.addHelper("initializerDefineProperty"), [types.cloneNode(path.get("left.object").node), types.stringLiteral(path.get("left.property").node.name || path.get("left.property").node.value), types.cloneNode(path.get("right.arguments.0").node), types.cloneNode(path.get("right.arguments.1").node)])); }, CallExpression(path, state) { if (path.node.arguments.length !== 3) return; if (!WARNING_CALLS.has(path.node.arguments[2])) return; if (path.node.callee.name !== state.addHelper("defineProperty").name) { return; } path.replaceWith(types.callExpression(state.addHelper("initializerDefineProperty"), [types.cloneNode(path.get("arguments.0").node), types.cloneNode(path.get("arguments.1").node), types.cloneNode(path.get("arguments.2.arguments.0").node), types.cloneNode(path.get("arguments.2.arguments.1").node)])); } }; const index = declare((api, options) => { api.assertVersion("^7.0.0-0 || ^8.0.0"); const { version } = options; if (version === "legacy") { return { name: "proposal-decorators", inherits: syntaxDecorators, visitor: visitor }; } else if (!version || version === "2023-11") { api.assertVersion("^7.0.2 || ^8.0.0"); return createClassFeaturePlugin({ name: "proposal-decorators", api, feature: FEATURES.decorators, inherits: syntaxDecorators, decoratorVersion: version }); } else { throw new Error("The '.version' option must be one of 'legacy' or '2023-11'."); } }); export { index as default }; //# sourceMappingURL=index.js.map