@lcap/builder
Version:
lcap builder utils
521 lines (520 loc) • 27.4 kB
JavaScript
;
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;