UNPKG

@babel/plugin-proposal-destructuring-private

Version:
484 lines (478 loc) 15.1 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var helperPluginUtils = require('@babel/helper-plugin-utils'); var core = require('@babel/core'); var pluginTransformDestructuring = require('@babel/plugin-transform-destructuring'); var pluginTransformParameters = require('@babel/plugin-transform-parameters'); const { assignmentExpression, binaryExpression, conditionalExpression, cloneNode, isObjectProperty, isPrivateName, memberExpression, numericLiteral, objectPattern, restElement, variableDeclarator, variableDeclaration, unaryExpression } = core.types; function buildUndefinedNode() { return unaryExpression("void", numericLiteral(0)); } function transformAssignmentPattern(initializer, tempId) { return conditionalExpression(binaryExpression("===", cloneNode(tempId), buildUndefinedNode()), initializer, cloneNode(tempId)); } function initRestExcludingKeys(pattern) { if (pattern.type === "ObjectPattern") { const { properties } = pattern; if (properties[properties.length - 1].type === "RestElement") { return []; } } return null; } function growRestExcludingKeys(excludingKeys, properties, scope) { if (excludingKeys === null) return; for (const property of properties) { const propertyKey = property.key; if (property.computed && !scope.isStatic(propertyKey)) { const tempId = scope.generateDeclaredUidIdentifier("m"); property.key = assignmentExpression("=", tempId, propertyKey); excludingKeys.push({ key: tempId, computed: true }); } else if (propertyKey.type !== "PrivateName") { excludingKeys.push(property); } } } function buildVariableDeclarationFromParams(params, scope) { const { elements, transformed } = buildAssignmentsFromPatternList(params, scope, false); return { params: elements, variableDeclaration: variableDeclaration("var", transformed.map(({ left, right }) => variableDeclarator(left, right))) }; } function buildAssignmentsFromPatternList(elements, scope, isAssignment) { const newElements = [], transformed = []; for (let element of elements) { if (element === null) { newElements.push(null); transformed.push(null); continue; } const tempId = scope.generateUidIdentifier("p"); if (isAssignment) { scope.push({ id: cloneNode(tempId) }); } if (element.type === "RestElement") { newElements.push(restElement(tempId)); element = element.argument; } else { newElements.push(tempId); } if (element.type === "AssignmentPattern") { transformed.push({ left: element.left, right: transformAssignmentPattern(element.right, tempId) }); } else { transformed.push({ left: element, right: cloneNode(tempId) }); } } return { elements: newElements, transformed }; } function* traversePattern(root, visitor) { const stack = []; stack.push({ node: root, index: 0, depth: 0 }); let item; while ((item = stack.pop()) !== undefined) { const { node, index } = item; if (node === null) continue; yield* visitor(node, index, item.depth); const depth = item.depth + 1; switch (node.type) { case "AssignmentPattern": stack.push({ node: node.left, index: 0, depth }); break; case "ObjectProperty": stack.push({ node: node.value, index, depth: item.depth }); break; case "RestElement": stack.push({ node: node.argument, index: 0, depth }); break; case "ObjectPattern": for (let list = node.properties, i = list.length - 1; i >= 0; i--) { stack.push({ node: list[i], index: i, depth }); } break; case "ArrayPattern": for (let list = node.elements, i = list.length - 1; i >= 0; i--) { stack.push({ node: list[i], index: i, depth }); } break; case "TSParameterProperty": case "TSAsExpression": case "TSTypeAssertion": case "TSNonNullExpression": throw new Error(`TypeScript features must first be transformed by ` + `@babel/plugin-transform-typescript.\n` + `If you have already enabled that plugin (or '@babel/preset-typescript'), make sure ` + `that it runs before @babel/plugin-proposal-destructuring-private.`); } } } function hasPrivateKeys(pattern) { let result = false; traversePattern(pattern, function* (node) { if (isObjectProperty(node) && isPrivateName(node.key)) { result = true; yield; } }).next(); return result; } function hasPrivateClassElement(node) { return node.body.some(element => isPrivateName(element.key)); } function* privateKeyPathIterator(pattern) { const indexPath = []; yield* traversePattern(pattern, function* (node, index, depth) { indexPath[depth] = index; if (isObjectProperty(node) && isPrivateName(node.key)) { yield indexPath.slice(1, depth + 1); } }); } function rightWillBeReferencedOnce(left) { switch (left.type) { case "Identifier": case "ArrayPattern": return true; case "ObjectPattern": return left.properties.length === 1; default: return false; } } function* transformPrivateKeyDestructuring(left, right, scope, isAssignment, shouldPreserveCompletion, addHelper, objectRestNoSymbols, useBuiltIns) { const stack = []; const rootRight = right; stack.push({ left, right, restExcludingKeys: initRestExcludingKeys(left) }); let item; while ((item = stack.pop()) !== undefined) { const { restExcludingKeys } = item; let { left, right } = item; const searchPrivateKey = privateKeyPathIterator(left).next(); if (searchPrivateKey.done) { if ((restExcludingKeys == null ? void 0 : restExcludingKeys.length) > 0) { const { properties } = left; if (properties.length === 1) { left = properties[0].argument; } yield { left: left, right: pluginTransformDestructuring.buildObjectExcludingKeys(restExcludingKeys, right, scope, addHelper, objectRestNoSymbols, useBuiltIns) }; } else { yield { left: left, right }; } } else { const indexPath = searchPrivateKey.value; for (let indexPathIndex = 0, index; indexPathIndex < indexPath.length && (index = indexPath[indexPathIndex]) !== undefined || left.type === "AssignmentPattern"; indexPathIndex++) { const isRightSafeToReuse = !(shouldPreserveCompletion && right === rootRight) && (rightWillBeReferencedOnce(left) || scope.isStatic(right)); if (!isRightSafeToReuse) { const tempId = scope.generateUidIdentifier("m"); if (isAssignment) { scope.push({ id: cloneNode(tempId) }); } yield { left: tempId, right }; right = cloneNode(tempId); } switch (left.type) { case "ObjectPattern": { const { properties } = left; if (index > 0) { const propertiesSlice = properties.slice(0, index); yield { left: objectPattern(propertiesSlice), right: cloneNode(right) }; } if (index < properties.length - 1) { const nextRestExcludingKeys = indexPathIndex === 0 ? restExcludingKeys : initRestExcludingKeys(left); growRestExcludingKeys(nextRestExcludingKeys, properties.slice(0, index + 1), scope); stack.push({ left: objectPattern(properties.slice(index + 1)), right: cloneNode(right), restExcludingKeys: nextRestExcludingKeys }); } const property = properties[index]; left = property.value; const { key } = property; const computed = property.computed || key.type !== "Identifier" && key.type !== "PrivateName"; right = memberExpression(right, key, computed); break; } case "AssignmentPattern": { right = transformAssignmentPattern(left.right, right); left = left.left; break; } case "ArrayPattern": { const leftElements = left.elements; const leftElementsAfterIndex = leftElements.splice(index); const { elements, transformed } = buildAssignmentsFromPatternList(leftElementsAfterIndex, scope, isAssignment); leftElements.push(...elements); yield { left, right: cloneNode(right) }; for (let i = transformed.length - 1; i > 0; i--) { if (transformed[i] !== null) { stack.push(transformed[i]); } } ({ left, right } = transformed[0]); break; } } } stack.push({ left, right, restExcludingKeys: initRestExcludingKeys(left) }); } } } var index = helperPluginUtils.declare(function ({ assertVersion, assumption, types: t }) { assertVersion("^7.17.0"); const { assignmentExpression, assignmentPattern, cloneNode, expressionStatement, isExpressionStatement, isIdentifier, isSequenceExpression, sequenceExpression, variableDeclaration, variableDeclarator } = t; const ignoreFunctionLength = assumption("ignoreFunctionLength"); const objectRestNoSymbols = assumption("objectRestNoSymbols"); const privateKeyDestructuringVisitor = { Function(path) { const firstPrivateIndex = path.node.params.findIndex(param => hasPrivateKeys(param)); if (firstPrivateIndex === -1) return; pluginTransformParameters.convertFunctionParams(path, ignoreFunctionLength, () => false); const { node, scope } = path; const { params } = node; const firstAssignmentPatternIndex = ignoreFunctionLength ? -1 : params.findIndex(param => param.type === "AssignmentPattern"); const paramsAfterIndex = params.splice(firstPrivateIndex); const { params: transformedParams, variableDeclaration } = buildVariableDeclarationFromParams(paramsAfterIndex, scope); path.get("body").unshiftContainer("body", variableDeclaration); params.push(...transformedParams); if (firstAssignmentPatternIndex >= firstPrivateIndex) { params[firstAssignmentPatternIndex] = assignmentPattern(params[firstAssignmentPatternIndex], scope.buildUndefinedNode()); } scope.crawl(); }, CatchClause(path) { const { node, scope } = path; if (!hasPrivateKeys(node.param)) return; const ref = scope.generateUidIdentifier("e"); path.get("body").unshiftContainer("body", variableDeclaration("let", [variableDeclarator(node.param, ref)])); node.param = cloneNode(ref); scope.crawl(); }, ForXStatement(path) { const { node, scope } = path; const leftPath = path.get("left"); if (leftPath.isVariableDeclaration()) { const left = leftPath.node; if (!hasPrivateKeys(left.declarations[0].id)) return; const temp = scope.generateUidIdentifier("ref"); node.left = variableDeclaration(left.kind, [variableDeclarator(temp, null)]); left.declarations[0].init = cloneNode(temp); pluginTransformDestructuring.unshiftForXStatementBody(path, [left]); scope.crawl(); } else if (leftPath.isPattern()) { if (!hasPrivateKeys(leftPath.node)) return; const temp = scope.generateUidIdentifier("ref"); node.left = variableDeclaration("const", [variableDeclarator(temp, null)]); const assignExpr = expressionStatement(assignmentExpression("=", leftPath.node, cloneNode(temp))); pluginTransformDestructuring.unshiftForXStatementBody(path, [assignExpr]); scope.crawl(); } }, VariableDeclaration(path, state) { const { scope, node } = path; const { declarations } = node; if (!declarations.some(declarator => hasPrivateKeys(declarator.id))) { return; } const newDeclarations = []; for (const declarator of declarations) { for (const { left, right } of transformPrivateKeyDestructuring(declarator.id, declarator.init, scope, false, false, name => state.addHelper(name), objectRestNoSymbols, true)) { newDeclarations.push(variableDeclarator(left, right)); } } node.declarations = newDeclarations; scope.crawl(); }, AssignmentExpression(path, state) { const { node, scope, parent } = path; if (!hasPrivateKeys(node.left)) return; const assignments = []; const shouldPreserveCompletion = !isExpressionStatement(parent) && !isSequenceExpression(parent) || path.isCompletionRecord(); for (const { left, right } of transformPrivateKeyDestructuring(node.left, node.right, scope, true, shouldPreserveCompletion, name => state.addHelper(name), objectRestNoSymbols, true)) { assignments.push(assignmentExpression("=", left, right)); } if (shouldPreserveCompletion) { const { left, right } = assignments[0]; if (isIdentifier(left) && right === node.right) { if (!isIdentifier(assignments[assignments.length - 1].right, { name: left.name })) { assignments.push(cloneNode(left)); } } else { const tempId = scope.generateDeclaredUidIdentifier("m"); assignments.unshift(assignmentExpression("=", tempId, cloneNode(node.right))); assignments.push(cloneNode(tempId)); } } path.replaceWith(sequenceExpression(assignments)); scope.crawl(); } }; const visitor = { Class(path, state) { if (!hasPrivateClassElement(path.node.body)) return; path.traverse(privateKeyDestructuringVisitor, state); } }; return { name: "proposal-destructuring-private", manipulateOptions: (_, p) => p.plugins.push("destructuringPrivate"), visitor: visitor }; }); exports.default = index; //# sourceMappingURL=index.js.map