UNPKG

@lcap/nasl

Version:

NetEase Application Specific Language

525 lines 25.9 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.parseNaturalTSXView = exports.transform2View = exports.transformJSXElement2ViewElement = exports.transform2Variables = void 0; /* eslint-disable global-require */ /* eslint-disable @typescript-eslint/no-use-before-define */ const utils_1 = require("./transforms/utils"); const lodash_1 = require("lodash"); const babel = __importStar(require("@babel/core")); const utils_2 = require("../utils"); const transform2TypeAnnotation_1 = require("./transforms/transform2TypeAnnotation"); const transform2LogicItem_1 = require("./transforms/transform2LogicItem"); const transform2Logic_1 = require("./transforms/transform2Logic"); const transform2ValidationRule_1 = require("./transforms/transform2ValidationRule"); const transformThemeAndStyle_1 = require("./transforms/transformThemeAndStyle"); function transform2Variables(node) { const variables = []; const declList = node?.declarations || []; declList.forEach((decl) => { const variableName = decl.id.name; let typeAnnotation = (0, transform2TypeAnnotation_1.transform2TypeAnnotation)(decl.id.typeAnnotation?.typeAnnotation); const variable = new utils_1.naslTypes.Variable({ name: variableName, typeAnnotation, }); if (decl.init) { variable.defaultValue = new utils_1.naslTypes.DefaultValue({ expression: (0, transform2LogicItem_1.transform2LogicItem)(decl.init, { transformType: 'attr', typeAnnotation, isRestricted: true, isInFrontend: true }), }); } variables.push(variable); }); return variables; } exports.transform2Variables = transform2Variables; function transform2EventLogics(node, eventName) { if (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') { return [(0, transform2Logic_1.transform2Logic)(node, eventName)]; } else if (node.type === 'CallExpression') { if (node.callee.type === 'Identifier' && node.callee.name === '$all') { const args = node.arguments; return args.map((node) => { if (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') { return (0, transform2Logic_1.transform2Logic)(node, eventName); } else { (0, utils_1.throwError)('事件逻辑参数不支持的类型', node.type, node); } }); } else { (0, utils_1.throwError)('事件逻辑参数不正确', node); } } else { (0, utils_1.throwError)('事件逻辑参数不正确', node); } } function transformJSXElement2ViewElement(node) { const el = node.openingElement; if (el.name.type === 'JSXIdentifier') { let tag = (0, lodash_1.kebabCase)(el.name.name); if (tag === 'el-form-date-time-picker') tag = 'el-form-date-picker'; if (tag === 'el-form-input-date') tag = 'el-form-date-picker'; if (tag === 'el-form-textarea') tag = 'el-form-input'; if (tag === 'el-form-item') tag = 'el-form-input'; const viewElement = new utils_1.naslTypes.ViewElement({ tag, }); const attrs = el.attributes; let directivePrefix = '$'; let eventPrefix = 'on'; let slotPrefix = 'slot'; function addJSXChild(node, slotTarget = 'default', slotScope = '') { if (node.type === 'JSXElement') { if (slotTarget === 'default' && !slotScope) { viewElement.children.push(transformJSXElement2ViewElement(node)); } else { viewElement.children.push(new utils_1.naslTypes.ViewElement({ tag: 'template', name: 'template_' + (0, utils_2.uuidv4)().slice(0, 5), slotTarget, slotScope, children: [transformJSXElement2ViewElement(node)], })); } } else if (node.type === 'JSXFragment') { const children = node.children; if (slotTarget === 'default' && !slotScope) { viewElement.children.push(...children.filter((child) => child.type === 'JSXElement').map(transformJSXElement2ViewElement)); } else { viewElement.children.push(new utils_1.naslTypes.ViewElement({ tag: 'template', name: 'template_' + (0, utils_2.uuidv4)().slice(0, 5), slotTarget, slotScope, children: children.filter((child) => child.type === 'JSXElement').map(transformJSXElement2ViewElement), })); } } else { (0, utils_1.throwError)('不支持的JSX子元素类型', node); } } function addArrowFunctionJSXChild(node, slotTarget = 'default') { const slotScope = node.params[0]?.name; const body = node.body; if (body.type === 'JSXElement' || body.type === 'JSXFragment') { addJSXChild(body, slotTarget, slotScope); } else if (body.type === 'ArrayExpression') { const elements = body.elements; elements.forEach((element) => { addJSXChild(element, slotTarget, slotScope); }); } else { (0, utils_1.throwError)('不支持的JSX子元素类型', body.type, body); } } attrs.forEach((attr) => { if (attr.type === 'JSXAttribute') { const attrName = attr.name.name; if (attrName === 'ref') { if (attr.value.type === 'StringLiteral') { viewElement.name = attr.value.value; } else { (0, utils_1.throwError)('ref 属性值不正确', attr.value); } } else if (attrName === 'style') { if (attr.value.type === 'StringLiteral') { viewElement.staticStyle = attr.value.value; } else { (0, utils_1.throwError)('style 属性只能用字符串字面量', attr.value); } } else if (attrName === '$extraStyle') { if (attr.value.type === 'StringLiteral') { viewElement.cssRules = (0, transformThemeAndStyle_1.transformStyle)(attr.value.value); } else if (attr.value.type === 'JSXExpressionContainer') { const templateLiteral = attr.value.expression; if (templateLiteral.type === 'TemplateLiteral') { if (templateLiteral.expressions.length > 0) { (0, utils_1.throwError)('不支持在 $theme 中使用动态表达式'); } const css = templateLiteral.quasis[0].value.raw; viewElement.cssRules = (0, transformThemeAndStyle_1.transformStyle)(css); } else { (0, utils_1.throwError)('$extraStyle 属性只能用字符串字面量', attr.value); } } else { (0, utils_1.throwError)('$extraStyle 属性只能用字符串字面量', attr.value); } } else if (attrName === '$dynamicStyle') { if (attr.value.type === 'JSXExpressionContainer') { const expression = attr.value.expression; if (expression.type === 'ObjectExpression') { const properties = expression.properties; properties.forEach((property) => { if (property.type === 'ObjectProperty') { const name = (0, lodash_1.kebabCase)(property.key.name); viewElement.bindStyles = viewElement.bindStyles || []; viewElement.addBindStyle(new utils_1.naslTypes.BindStyle({ name, expression: (0, transform2LogicItem_1.transform2LogicItem)(property.value, { transformType: 'attr', isRestricted: true, isInFrontend: true, }), })); } }); } } else { (0, utils_1.throwError)('style 属性值不正确', attr.value); } } else if (attrName.startsWith(directivePrefix)) { const directiveName = attrName.slice(directivePrefix.length); const directive = new utils_1.naslTypes.BindDirective({ name: directiveName, }); if (attr.value.type === 'StringLiteral') { directive.type = 'string'; directive.value = attr.value.value; } else if (attr.value.type === 'JSXExpressionContainer') { if (attr.value.expression.type === 'BooleanLiteral' || attr.value.expression.type === 'NumericLiteral' || attr.value.expression.type === 'NullLiteral' || attr.value.expression.type === 'StringLiteral') { directive.type = 'static'; directive.value = (0, utils_1.generate)(attr.value.expression).code; } else { directive.type = 'dynamic'; if (attr.value.expression.type === 'FunctionExpression') (0, utils_1.throwError)('指令不支持传函数表达式', attr); directive.expression = (0, transform2LogicItem_1.transform2LogicItem)(attr.value.expression, { transformType: 'attr', isRestricted: true, isInFrontend: true, }); } } else { (0, utils_1.throwError)('不支持的指令类型', attr.value.type, attr.value); } if (directiveName !== 'for') { viewElement.addBindDirective(directive); } } else if (attrName.startsWith(eventPrefix)) { const eventName = (0, utils_2.firstLowerCase)(attrName.slice(eventPrefix.length)); if (attr.value.type === 'JSXExpressionContainer') { const bindEvent = new utils_1.naslTypes.BindEvent({ name: eventName, logics: transform2EventLogics(attr.value.expression, eventName), }); viewElement.addBindEvent(bindEvent); } else { (0, utils_1.throwError)('事件逻辑参数不正确', attr.value); } } else if (attrName.startsWith(slotPrefix)) { const slotTarget = (0, utils_2.firstLowerCase)(attrName.slice(slotPrefix.length)); if (attr.value.type === 'JSXExpressionContainer') { const expression = attr.value.expression; if (expression.type === 'JSXElement' || expression.type === 'JSXFragment') { addJSXChild(expression, slotTarget); } else if (expression.type === 'ArrowFunctionExpression') { addArrowFunctionJSXChild(expression, slotTarget); } else { (0, utils_1.throwError)('slot 属性值不正确', attr.value); } } } else { const bindAttribute = new utils_1.naslTypes.BindAttribute({ name: attrName, }); if (attr.value.type === 'StringLiteral') { bindAttribute.type = 'string'; bindAttribute.value = attr.value.value; } else if (attr.value.type === 'JSXExpressionContainer') { if (attrName === 'rules') { if (attr.value.expression.type === 'ArrayExpression') { const elements = attr.value.expression.elements; bindAttribute.rules = elements.map(transform2ValidationRule_1.transform2ValidationRule); } else if (attr.value.expression.type === 'CallExpression') { const callee = (0, utils_1.generate)(attr.value.expression.callee).code; if (callee === 'nasl.util.NewList' || callee === 'NewList') { const elements = attr.value.expression.arguments[0].elements; bindAttribute.rules = elements.map(transform2ValidationRule_1.transform2ValidationRule); } else { (0, utils_1.throwError)('rules 属性值不正确', attr.value); } } else { (0, utils_1.throwError)('rules 属性值不正确', attr.value); } } else if (attr.value.expression.type === 'BooleanLiteral' || attr.value.expression.type === 'NumericLiteral' || attr.value.expression.type === 'NullLiteral' || attr.value.expression.type === 'StringLiteral') { bindAttribute.type = 'static'; bindAttribute.value = (0, utils_1.generate)(attr.value.expression).code; } else { let expr = attr.value.expression; if (attrName === 'prop' || attrName.endsWith('Field')) { if (expr.type === 'ArrowFunctionExpression') { bindAttribute.type = 'string'; const paramName = expr.params[0]?.name || 'item'; const code = (0, utils_1.generate)(expr.body).code; bindAttribute.value = code.replace(new RegExp(`${paramName}\\.`, 'g'), ''); if (bindAttribute.value === 'item') bindAttribute.value = ''; viewElement.addBindAttribute(bindAttribute); return; } } bindAttribute.type = 'dynamic'; if (expr.type === 'CallExpression' && expr.callee.type === 'Identifier' && expr.callee.name === '$sync') { bindAttribute.sync = true; expr = expr.arguments[0]; } if (expr.type === 'FunctionExpression') (0, utils_1.throwError)('属性不支持传函数表达式', attr); const expression = (0, transform2LogicItem_1.transform2LogicItem)(expr, { transformType: 'attr', isRestricted: attrName !== 'dataSource', isInFrontend: true, }); if (expression?.concept === 'Destination') { bindAttribute.destination = expression; } else if (expression?.concept === 'ExternalDestination') { bindAttribute.externalDestination = expression; } else { bindAttribute.expression = expression; } } } else { (0, utils_1.throwError)('不支持的指令类型', attr.value.type, attr.value); } viewElement.addBindAttribute(bindAttribute); } } else { (0, utils_1.throwError)('不支持的JSX属性类型', attr.type, attr); } }); node.children.forEach((child) => { if (child.type === 'JSXElement' || child.type === 'JSXFragment') { addJSXChild(child); } else if (child.type === 'JSXExpressionContainer') { const expression = child.expression; if (expression.type === 'JSXElement' || expression.type === 'JSXFragment') { addJSXChild(expression); } else if (expression.type === 'ArrowFunctionExpression') { addArrowFunctionJSXChild(expression); } else if (expression.type === 'JSXEmptyExpression') { // ignore } else { (0, utils_1.throwError)('不支持的JSX子元素类型', expression.type, expression); } } else if (child.type === 'JSXText') { // ignore } else { (0, utils_1.throwError)('不支持的JSX子元素类型', child.type, child); } }); if (!viewElement.name) { viewElement.name = viewElement.tag.replace(/-/g, '_') + '_' + (0, utils_2.uuidv4)().slice(0, 5); } return viewElement; } else { (0, utils_1.throwError)('不支持的JSX元素类型', el.name.type, el); } } exports.transformJSXElement2ViewElement = transformJSXElement2ViewElement; function transform2View(func, decorator) { const statements = func.body.body; const view = new utils_1.naslTypes.View({ name: func.id?.name, }); if (decorator) { if (decorator.arguments[0].type === 'ObjectExpression') { const viewObj = (0, utils_1.pickDecoratorObject)(decorator.arguments[0], new Set([ 'pageTemplateId', 'uuid', 'title', 'crumb', 'auth', 'authDescription', 'isIndex', ])); Object.assign(view, viewObj); view.crumb = new utils_1.naslTypes.StaticString({ value: viewObj.crumb, }); } else { (0, utils_1.throwError)('不支持的装饰器类型', decorator.type, decorator); } } func.params.forEach((param) => { if (param.type === 'Identifier') { view.params.push((0, transform2LogicItem_1.transform2Param)(param)); } else if (param.type === 'ObjectPattern') { const propertyMap = {}; param.properties.forEach((property) => { if (property.type === 'ObjectProperty') { if (property.value.type === 'Identifier') { propertyMap[property.value.name] = { type: 'AssignmentPattern', left: property.value, right: undefined, }; } else if (property.value.type === 'AssignmentPattern') { propertyMap[property.value.left.name] = property.value; } else { (0, utils_1.throwError)('不支持的参数类型', property.value.type, property.value); } } }); const type = param.typeAnnotation?.typeAnnotation; if (!type) (0, utils_1.throwError)('param 没有对应的类型', param); type.members.forEach((member) => { if (member.type === 'TSPropertySignature') { const name = member.key.name; if (propertyMap[name]) { propertyMap[name].left.typeAnnotation = member.typeAnnotation; } } }); Object.values(propertyMap).forEach((property) => { view.params.push((0, transform2LogicItem_1.transform2Param)(property)); }); } }); statements.forEach((statement) => { if (statement.type === 'VariableDeclaration') { const variables = transform2Variables(statement); variables.forEach((variable) => { view.addVariable(variable); }); } else if (statement.type === 'FunctionDeclaration') { const logic = (0, transform2Logic_1.transform2Logic)(statement); view.addLogic(logic); } else if (statement.type === 'ExpressionStatement' && statement.expression.type === 'CallExpression') { const expression = statement.expression; if (expression.callee.type == 'Identifier' && expression.callee.name.startsWith('on')) { const eventName = (0, utils_2.firstLowerCase)(expression.callee.name.slice(2)); const arg = expression.arguments[0]; const bindEvent = new utils_1.naslTypes.BindEvent({ name: eventName, logics: transform2EventLogics(arg, eventName), }); view.addBindEvent(bindEvent); } else { (0, utils_1.throwError)('不支持的表达式', statement); } } else if (statement.type === 'ReturnStatement') { const arg = statement.argument; if (arg.type === 'JSXElement') { view.elements = [transformJSXElement2ViewElement(arg)]; } else if (arg.type === 'JSXFragment') { view.elements = arg.children.filter((child) => child.type === 'JSXElement').map(transformJSXElement2ViewElement); } else { (0, utils_1.throwError)('返回值不正确', arg); } } else { (0, utils_1.throwError)('不支持的表达式', statement); } }); return view; } exports.transform2View = transform2View; function parseNaturalTSXView(tsCode) { const root = babel.parseSync(tsCode, { filename: 'result.tsx', presets: [require('@babel/preset-typescript')], }); let func; let funcIndex = 0; let decorator; root.program.body.forEach((statement, index) => { if (statement.type === 'ExportNamedDeclaration' && statement.declaration.type === 'FunctionDeclaration') { func = statement.declaration; funcIndex = index; } else if (statement.type === 'FunctionDeclaration') { func = statement; funcIndex = index; } const prevStatement = root.program.body[index - 1]; if (prevStatement?.type === 'ExpressionStatement' && prevStatement.expression.type === 'CallExpression' && prevStatement.expression.callee.type === 'Identifier' && prevStatement.expression.callee.name === '$View') { decorator = prevStatement.expression; } }); return transform2View(func, decorator); } exports.parseNaturalTSXView = parseNaturalTSXView; //# sourceMappingURL=parseNaturalTSXView.js.map