UNPKG

@lcap/builder

Version:
521 lines (520 loc) 27.4 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; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); /* eslint-disable no-useless-escape */ /* eslint-disable no-plusplus */ /* eslint-disable consistent-return */ /* eslint-disable global-require */ /* eslint-disable no-use-before-define */ /* eslint-disable no-nested-ternary */ /* eslint-disable @typescript-eslint/ban-ts-comment */ const babel = __importStar(require("@babel/core")); const babelTypes = __importStar(require("@babel/types")); const generator_1 = __importDefault(require("@babel/generator")); const babel_utils_1 = require("../utils/babel-utils"); const snippet_code2nasl_1 = __importDefault(require("../nasl/snippet-code2nasl")); const string_1 = require("../utils/string"); function getValueFromObjectExpressionByKey(object, key) { const property = object.properties.find((prop) => prop.type === 'ObjectProperty' && prop.key.name === key); if (!property) return undefined; return (0, generator_1.default)(property.value).code; } const isPrimitive = (name) => ['String', 'Integer', 'Decimal', 'Boolean'].includes(name); const getMemberExpressionName = (node) => { if (node.object.type === 'MemberExpression') { // @ts-ignore return `${getMemberExpressionName(node.object)}.${node.property.name}`; } // @ts-ignore return `${node.object.name}.${node.property.name}`; }; // eslint-disable-next-line consistent-return function transformTypeAnnotation(node) { var _a, _b; if (node.type === 'TSTypeReference') { const { typeName, typeParameters } = node; if (typeParameters && typeName.type === 'TSQualifiedName') { const { left, right } = typeName; const primitive = isPrimitive(right.name); const namespace = `${left.left.name}.${left.right.name}`; return { typeKind: ((_a = typeParameters === null || typeParameters === void 0 ? void 0 : typeParameters.params) === null || _a === void 0 ? void 0 : _a.length) ? 'generic' : primitive ? 'primitive' : 'reference', typeName: right.name, typeNamespace: namespace, concept: 'TypeAnnotation', inferred: false, ruleMap: new Map(), typeArguments: transformTypeParameters(typeParameters), }; } if (typeParameters && typeName.type === 'Identifier') { const primitive = isPrimitive(typeName.name); return { typeKind: ((_b = typeParameters === null || typeParameters === void 0 ? void 0 : typeParameters.params) === null || _b === void 0 ? void 0 : _b.length) ? 'generic' : primitive ? 'primitive' : 'reference', typeName: typeName.name, typeNamespace: ['Current', 'CurrentDynamic'].includes(typeName.name) ? 'nasl.ui' : '', concept: 'TypeAnnotation', inferred: false, ruleMap: new Map(), typeArguments: transformTypeParameters(typeParameters), }; } } } function transformTypeParameters(typeParameters) { var _a; return (((_a = typeParameters === null || typeParameters === void 0 ? void 0 : typeParameters.params) === null || _a === void 0 ? void 0 : _a.map((param) => { return Object.assign(Object.assign({}, transformTypeAnnotation(param)), { concept: 'TypeAnnotation', typeKind: 'typeParam', inferred: false, ruleMap: new Map() }); })) || []); } function transformSlotParams(params) { return (params || []).map((param) => { const typeAnnotation = param.typeAnnotation; return { concept: 'Param', name: param.name, description: '', typeAnnotation: transformTypeAnnotation(typeAnnotation.typeAnnotation), }; }); } function transformValue(node, typeAnnotation) { if (node.type === 'NullLiteral') { return { concept: 'NullLiteral', }; } if (node.type === 'StringLiteral') { return { concept: 'StringLiteral', value: node.value, }; } if (node.type === 'BooleanLiteral') { return { concept: 'BooleanLiteral', value: String(node.value), }; } if (node.type === 'UnaryExpression' && node.operator === '-' && node.prefix && node.argument.type === 'NumericLiteral') { const value = `-${String(node.argument.value)}`; return { concept: 'NumericLiteral', value, typeAnnotation: { concept: 'TypeAnnotation', typeKind: 'primitive', typeName: value.includes('.') ? 'Decimal' : 'Integer', typeNamespace: 'nasl.core', inferred: false, ruleMap: new Map(), }, }; } if (node.type === 'NumericLiteral') { const value = String(node.value); return { concept: 'NumericLiteral', value, typeAnnotation: { concept: 'TypeAnnotation', typeKind: 'primitive', typeName: value.includes('.') ? 'Decimal' : 'Integer', typeNamespace: 'nasl.core', inferred: false, ruleMap: new Map(), }, }; } if (node.type === 'ArrayExpression') { return { concept: 'NewList', items: node.elements.map((item) => transformValue(item)).filter(Boolean), typeAnnotation: transformTypeAnnotation(typeAnnotation), }; } if (node.type === 'TSAsExpression') { if (node.expression) { if (node.expression.type === 'ArrowFunctionExpression') { const { body } = node.expression; if (body.type === 'MemberExpression') { let value = getMemberExpressionName(body); value = value .split('.') .slice(1) .join('.'); return { concept: 'StringLiteral', value, }; } } else { return transformValue(node.expression, node.typeAnnotation); } } } } function transform(tsCode, framework) { const root = babel.parseSync(tsCode, { filename: 'result.ts', presets: [require('@babel/preset-typescript')], plugins: [ [require('@babel/plugin-proposal-decorators'), { legacy: true }], // 'babel-plugin-parameter-decorator' ], rootMode: 'root', root: __dirname, }); const programBody = root.program.body; let blockOrDecl = programBody.find((stat) => stat.type === 'TSModuleDeclaration'); while (blockOrDecl && blockOrDecl.type === 'TSModuleDeclaration') { blockOrDecl = blockOrDecl.body; } if (!blockOrDecl) return []; const mainBody = blockOrDecl.body; /** * 组件名:[组件 class, 组件选项 class] */ const classMap = new Map(); mainBody.forEach((statement) => { var _a; let classDecl; if (statement.type === 'ExportNamedDeclaration') { classDecl = statement.declaration; } else if (statement.type === 'ClassDeclaration') { classDecl = statement; } else { return; } if (classDecl.id && classDecl.id.name.endsWith('Options')) { const className = classDecl.id.name.replace(/Options$/, ''); let classItem = classMap.get(className); if (!classItem) { classItem = [null, classDecl]; classMap.set(className, classItem); } else { classItem[1] = classDecl; } } else { const className = (_a = classDecl === null || classDecl === void 0 ? void 0 : classDecl.id) === null || _a === void 0 ? void 0 : _a.name; if (classDecl.superClass.name === 'ViewComponent') { let classItem = classMap.get(className); if (!classItem) { classItem = [classDecl, null]; classMap.set(className, classItem); } else { classItem[0] = classDecl; } } } }); // forEach classMap const result = []; let index = 0; classMap.forEach((classItem, className) => { const [classDecl, optionsDecl] = classItem; if (!classDecl) return; const component = { concept: 'ViewComponentDeclaration', group: '', // VanRouterView name: className, // van-router-view kebabName: (0, string_1.getKebabCaseName)(className), title: '', description: '', icon: '', tsTypeParams: undefined, props: [], readableProps: [], events: [], methods: [], slots: [], children: [], blocks: [], themeVariables: [], }; classDecl.decorators.forEach((decorator) => { if (decorator.expression.type === 'CallExpression' && decorator.expression.callee.name === 'Component') { decorator.expression.arguments.forEach((arg) => { if (arg.type === 'ObjectExpression') Object.assign(component, (0, babel_utils_1.evalOptions)(arg)); }); } else if (decorator.expression.type === 'CallExpression' && (decorator.expression.callee.name === 'ExtensionComponent' || decorator.expression.callee.name === 'IDEExtraInfo')) { decorator.expression.arguments.forEach((arg) => { if (arg.type === 'ObjectExpression') { Object.assign(component, (0, babel_utils_1.evalOptions)(arg)); } }); } }); { const classTypeParams = (0, generator_1.default)(babelTypes.tsInterfaceDeclaration(babelTypes.identifier('Wrapper'), classDecl.typeParameters, [], babelTypes.tsInterfaceBody([]))) .code.replace(/^interface Wrapper<?/, '') .replace(/>? {}$/, ''); const optionsTypeParams = (0, generator_1.default)(babelTypes.tsInterfaceDeclaration(babelTypes.identifier('Wrapper'), optionsDecl.typeParameters, [], babelTypes.tsInterfaceBody([]))) .code.replace(/^interface Wrapper<?/, '') .replace(/>? {}$/, ''); if (classTypeParams !== optionsTypeParams) throw new Error(`组件的${className}泛型类型参数不匹配!`); component.tsTypeParams = classTypeParams; } optionsDecl.body.body.forEach((member) => { var _a, _b, _c, _d; if (member.type === 'ClassProperty' && member.decorators) { for (let i = 0; i < member.decorators.length; i++) { const decorator = member.decorators[i]; if (decorator.expression.type === 'CallExpression') { const calleeName = decorator.expression.callee.name; if (calleeName === 'Prop') { // 过滤private属性 if (member.accessibility === 'private') { // eslint-disable-next-line no-continue continue; } const prop = { concept: 'PropDeclaration', group: getValueFromObjectExpressionByKey(decorator.expression.arguments[0], 'group') || '主要属性', sync: getValueFromObjectExpressionByKey(decorator.expression.arguments[0], 'sync'), bindHide: getValueFromObjectExpressionByKey(decorator.expression.arguments[0], 'bindHide'), bindOpen: getValueFromObjectExpressionByKey(decorator.expression.arguments[0], 'bindOpen'), tabKind: getValueFromObjectExpressionByKey(decorator.expression.arguments[0], 'tabKind') || 'property', setter: getValueFromObjectExpressionByKey(decorator.expression.arguments[0], 'setter'), name: member.key.name, title: getValueFromObjectExpressionByKey(decorator.expression.arguments[0], 'title'), tsType: (0, generator_1.default)(member.typeAnnotation.typeAnnotation).code, }; // 默认值 if (member.value) { const typeAnnotation = member.typeAnnotation; prop.defaultValue = { concept: 'DefaultValue', expression: transformValue(member.value, typeAnnotation.typeAnnotation), playground: [], }; } // @TODO: default // @TODO: private decorator.expression.arguments.forEach((arg) => { if (arg.type === 'ObjectExpression') Object.assign(prop, (0, babel_utils_1.evalOptions)(arg)); }); // 枚举类型生成选项 if (['EnumSelectSetter', 'CapsulesSetter'].includes((_a = prop === null || prop === void 0 ? void 0 : prop.setter) === null || _a === void 0 ? void 0 : _a.concept)) { // 因为converter里有'join:|',所有这里的分割前后需要空格 const types = prop === null || prop === void 0 ? void 0 : prop.tsType.replace(/\(|\)|\[\]|Array\<|\>/g, '').split(' | ').map((type) => { try { return eval(type.trim()); } catch (e) { } return undefined; }); // @ts-ignore (_b = prop === null || prop === void 0 ? void 0 : prop.setter) === null || _b === void 0 ? void 0 : _b.options = (_d = (_c = prop === null || prop === void 0 ? void 0 : prop.setter) === null || _c === void 0 ? void 0 : _c.options) === null || _d === void 0 ? void 0 : _d.map((option, idx) => { var _a; if (option.if) { option.if = option.if.toString(); option.tsIf = option.if; } if (option.disabledIf) { option.disabledIf = option.disabledIf.toString(); option.tsDisabledIf = option.disabledIf; } let value = (_a = option.value) !== null && _a !== void 0 ? _a : types[idx]; return Object.assign(Object.assign({}, option), { value }); }); } if (['_alignment', '_justify'].includes(prop.name)) { // 去掉_ prop.name = prop.name.replace(/^_/, ''); } component.props.push(prop); } else if (calleeName === 'Event') { const event = { concept: 'EventDeclaration', name: member.key.name.replace(/^on(\w+)/, ($0, $1) => { return $1.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); }), title: '', tsType: (0, generator_1.default)(member.typeAnnotation.typeAnnotation).code, }; decorator.expression.arguments.forEach((arg) => { if (arg.type === 'ObjectExpression') Object.assign(event, (0, babel_utils_1.evalOptions)(arg)); }); component.events.push(event); } else if (calleeName === 'Slot') { const parameters = member.typeAnnotation.typeAnnotation.parameters; let slotName; if (member.key.type === 'Identifier') { slotName = member.key.name; } else if (member.key.type === 'StringLiteral') { slotName = member.key.value; } const slot = { concept: 'SlotDeclaration', snippets: getValueFromObjectExpressionByKey(decorator.expression.arguments[0], 'snippets'), name: (0, babel_utils_1.getSlotName)(slotName), title: '', tsType: (0, generator_1.default)(member.typeAnnotation.typeAnnotation).code, params: transformSlotParams(parameters), }; decorator.expression.arguments.forEach((arg) => { if (arg.type === 'ObjectExpression') Object.assign(slot, (0, babel_utils_1.evalOptions)(arg)); }); if (Array.isArray(slot.snippets) && slot.snippets.length > 0 && !framework.startsWith('vue')) { slot.snippets = slot.snippets.map((snippetConfig) => (Object.assign(Object.assign({}, snippetConfig), { code: (0, snippet_code2nasl_1.default)(snippetConfig.code) }))); } component.slots.push(slot); } } } } }); classDecl.body.body.forEach((member) => { var _a, _b; if ((member.type === 'ClassProperty' || member.type === 'ClassMethod') && member.accessibility === 'private') { return; } if (member.type === 'ClassProperty') { (_a = member.decorators) === null || _a === void 0 ? void 0 : _a.forEach((decorator) => { if (decorator.expression.type === 'CallExpression') { const calleeName = decorator.expression.callee.name; if (calleeName === 'Prop') { const prop = { concept: 'PropDeclaration', group: getValueFromObjectExpressionByKey(decorator.expression.arguments[0], 'group') || '主要属性', sync: getValueFromObjectExpressionByKey(decorator.expression.arguments[0], 'sync'), bindHide: getValueFromObjectExpressionByKey(decorator.expression.arguments[0], 'bindHide'), bindOpen: getValueFromObjectExpressionByKey(decorator.expression.arguments[0], 'bindOpen'), tabKind: getValueFromObjectExpressionByKey(decorator.expression.arguments[0], 'tabKind') || 'property', setter: getValueFromObjectExpressionByKey(decorator.expression.arguments[0], 'setter'), name: member.key.name, title: getValueFromObjectExpressionByKey(decorator.expression.arguments[0], 'title'), tsType: (0, generator_1.default)(member.typeAnnotation.typeAnnotation).code, }; // @TODO: default // @TODO: private decorator.expression.arguments.forEach((arg) => { if (arg.type === 'ObjectExpression') Object.assign(prop, (0, babel_utils_1.evalOptions)(arg)); }); component.readableProps.push(prop); } } }); } else if (member.type === 'ClassMethod') { (_b = member.decorators) === null || _b === void 0 ? void 0 : _b.forEach((decorator) => { if (decorator.expression.type === 'CallExpression') { const calleeName = decorator.expression.callee.name; if (calleeName === 'Method') { const method = { concept: 'LogicDeclaration', description: getValueFromObjectExpressionByKey(decorator.expression.arguments[0], 'description'), params: member.params.map((param) => { const { type } = param; // 有默认值 if (type === 'AssignmentPattern') { const decorator = param.left.decorators ? param.left.decorators[0] : null; const typeAnnotation = param.left.typeAnnotation; return { concept: 'Param', name: param.left.name, description: decorator ? getValueFromObjectExpressionByKey(decorator.expression.arguments[0], 'description') : param.left.name, typeAnnotation: transformTypeAnnotation(typeAnnotation.typeAnnotation), defaultValue: { concept: 'DefaultValue', expression: transformValue(param.right, typeAnnotation.typeAnnotation), playground: [], }, }; } // eslint-disable-next-line no-shadow const decorator = param.decorators ? param.decorators[0] : null; const typeAnnotation = param.typeAnnotation; return { concept: 'Param', name: param.name, optional: param.optional, description: decorator ? getValueFromObjectExpressionByKey(decorator.expression.arguments[0], 'description') : param.name, typeAnnotation: transformTypeAnnotation(typeAnnotation.typeAnnotation), }; }), // TODO returns: [], name: member.key.name, title: getValueFromObjectExpressionByKey(decorator.expression.arguments[0], 'title'), // type: generate((member. as babelTypes.TSTypeAnnotation).typeAnnotation).code, }; method.tsType = `(${member.params.map((param, index) => { if (param.type === 'AssignmentPattern' && param.left.type === 'Identifier') { return `${param.left.name}: ${param.left.typeAnnotation && param.left.typeAnnotation.type === 'TSTypeAnnotation' && param.left.typeAnnotation.typeAnnotation ? (0, generator_1.default)(param.left.typeAnnotation.typeAnnotation).code : 'any'}`; } if (param.type === 'Identifier') { return `${param.name}: ${param.typeAnnotation && param.typeAnnotation.type === 'TSTypeAnnotation' && param.typeAnnotation.typeAnnotation ? (0, generator_1.default)(param.typeAnnotation.typeAnnotation).code : 'any'}`; } return `param${index + 1}`; }).join(', ')}) => ${member.returnType && member.returnType.type === 'TSTypeAnnotation' && member.returnType.typeAnnotation ? (0, generator_1.default)(member.returnType.typeAnnotation).code : 'void'}`; decorator.expression.arguments.forEach((arg) => { if (arg.type === 'ObjectExpression') Object.assign(method, (0, babel_utils_1.evalOptions)(arg)); }); component.methods.push(method); } } }); } }); if (index === 0) { result.push(component); } else { const parent = result[0]; parent.children.push(component); } index++; }); return result; } exports.default = transform;