UNPKG

@lcap/nasl

Version:

NetEase Application Specific Language

1,197 lines 53 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.checkAStructure_ = exports.naslNodeTranslateMessage = exports.translateDiagnosticMessage = exports.outOriginalvalue = void 0; const naslServer_1 = require("./naslServer"); const nasl_concepts_1 = require("@lcap/nasl-concepts"); const sortTsString_1 = require("../utils/sortTsString"); const asserts_1 = require("@lcap/nasl-concepts/asserts"); const mapAstString = new Map(); function transformType(tsType) { tsType = tsType.replaceAll(' & { __name: "AStructure"; }', '').replaceAll('_AStructure', 'AStructure'); if (tsType === 'string' || tsType === 'String' || /StringLiteral<['"].*['"]>/.test(tsType)) return '字符串'; else if (tsType === 'number') return '数字'; else if (tsType === 'Integer') return '整数'; else if (tsType === 'Double') return '小数'; else if (tsType === 'Decimal') return '小数'; else if (tsType === 'Long') return '整数'; else if (tsType === 'DateTime') return '日期时间'; else if (tsType === 'Date') return '日期'; else if (tsType === 'Time') return '时间'; else if (tsType === 'boolean' || tsType === 'Boolean') return '布尔值'; else if (tsType === 'unknown') return '未知类型'; else if (tsType === 'Enums') return '枚举'; else if (tsType === 'views') return '页面'; else if (tsType === 'void') return '无返回值'; else if (tsType === '__elements') return '页面元素'; else if (tsType === '__unknown__') return '无效'; else if (tsType === 'extensions') return '拓展模块'; else if (tsType === 'Promise<void>') return '未知类型'; else if ((tsType.startsWith('{') && tsType.endsWith('}')) || (tsType.startsWith('{') && tsType.endsWith('...'))) { if (tsType.includes('__name: "AStructure_') || tsType.includes('__name: any')) { return tsType.replace(/__name: "AStructure_\w{8}";/g, '').replace(/__name: any;/g, ''); } return '当前节点'; } else if (tsType.endsWith('[]')) { return `List<${transformType(tsType.slice(0, -2))}>`; } else if (tsType.startsWith('<T extends ') && tsType.endsWith('>() => T')) { const types = (0, naslServer_1.getDisplayString2Type)(tsType); let tsStr = ''; types.forEach((item, index) => { tsStr += index === 0 ? transformType(item) : ' | ' + transformType(item); }); return tsStr; } else if (tsType.includes(' & ')) { const types = tsType.split(' & '); let tsStr = ''; types.forEach((item, index) => { tsStr += index === 0 ? transformType(item) : ' & ' + transformType(item); }); return tsStr; } else if (tsType.includes(' | ')) { const types = tsType.split(' | '); let tsStr = ''; types.forEach((item, index) => { const newItem = item.replace(/[a-zA-Z_]+/g, (text) => transformType(text)); tsStr += index === 0 ? newItem : ' 或 ' + newItem; }); return tsStr; } else { return tsType; } } const NAMESPACE_CONCEPT_MAP = { entities: '实体', structures: '数据结构', enums: '枚举', logics: '逻辑', interfaces: '接口', views: '页面', elements: '元素', processes: '流程', processV2s: '流程', roles: '角色', extensions: '扩展模块', apis: '接口应用', components: '扩展组件', properties: '配置参数', }; function transformNamespace(tsNamespace) { for (const key in NAMESPACE_CONCEPT_MAP) { const value = NAMESPACE_CONCEPT_MAP[key]; if (tsNamespace.endsWith(key)) return value; } return tsNamespace; } function transformMethod(tsMethod) { if (tsMethod === 'forEach') return 'ForEach'; else return tsMethod; } function transformIdentifier(identifier) { if (NAMESPACE_CONCEPT_MAP[identifier]) return NAMESPACE_CONCEPT_MAP[identifier]; else return ` ${transformType(identifier)} 上的`; } function outOriginalvalue(value) { return value; } exports.outOriginalvalue = outOriginalvalue; const TS_RULES_TYPE_INCONSISTENCY = [ /This condition will always return '(.+?)' since the types '(.+?)' and '(.+?)' have no overlap/, /Property '(.+?)' is missing in type '(.+?)' but required in type '(.+?)'/, /Argument of type '(.+?)' is not assignable to parameter of type '(.+?)'/, /Type '(.+?)' is not assignable to type '(.+?)'/, /Type '(.+?)' does not satisfy the constraint '(.+?)'/, /Type '(.+?)' is missing the following properties from type '(.+?)'/, ]; const TS_RULES = [ { re: /Property '(FrontEnd|BackEnd)(.+?)' is private and only accessible within/, result: '找不到配置参数$2', transforms: [transformType, transformType], }, { re: /Property '(.+?)' is private and only accessible within class '(.+?)'./, result: '“$1”是私有的,只能在“$2”中访问。', transforms: [transformType, transformType], }, { re: /Object literal may only specify known properties, and '(.+?)' does not exist in type/, result: '当前参数$1,不可用。', transforms: [transformType], }, { re: /Object literal may only specify known properties, but '(.+?)' does not exist in type/, result: '当前参数$1,不可用。', transforms: [transformType], }, { re: /Argument of type '(.+?)' is not assignable to parameter of type '(.+?)'/, result: '参数类型不一致!传入类型:$1,接收类型:$2。', transforms: [transformType, transformType], }, { re: /Type '(.+?)' is not assignable to type '(.+?)'/, result: '左右参数类型不一致!左边类型:$2,右边类型:$1。', transforms: [transformType, transformType], }, { re: /Type '(.+?)' does not satisfy the constraint '(.+?)'/, result: '参数类型不一致!传入类型:$1,接收类型:$2。', transforms: [transformType, transformType], }, { re: /This condition will always return '(.+?)' since the types '(.+?)' and '(.+?)' have no overlap/, result: '左右参数类型不一致!左边类型:$2,右边类型:$3。', transforms: [transformType, transformType, transformType], }, { re: /Type '(.+?)' is missing the following properties from type '(.+?)'/, result: '类型不一致!左边类型:$2,右边类型:$1。', transforms: [transformType, transformType], }, { re: /Property '(.+?)' is missing in type '(.+?)' but required in type '(.+?)'/, result: '类型不一致!左边类型:$3,右边类型:$2。', transforms: [outOriginalvalue, transformType, transformType], }, { re: /object literal cannot have multiple properties with the same name/, result: '不能具有多个名称相同的属性。', }, { re: /Invalid character/, result: '无效的字符', }, { // This expression is not callable.\n Type 'Entity1' has no call signatures. re: /This expression is not callable.\s*Type '(.+?)' has no call signatures/, result: '此表达式不可用。', }, { re: /Expression expected/, result: '表达式不能为空!', }, { re: /Cannot find name '__(?:IDENTIFIER|LEFT|RIGHT)__'/, result: '表达式不能为空!', }, { re: /Cannot find name '__(?:NOREFCONNECTION)__'/, result: '文件存储配置不能为空!', }, { re: /Cannot find name '__(?:FRONTENDVARIABLES)__'/, result: '前端全局变量:不可在非页面下使用。', }, { re: /Cannot find name '__(?:QueryFieldExpression)__'/, result: '数据查询:实体属性不能为空。', }, { re: /Cannot find name '__(?:QueryFieldExpressionMissing)__'/, result: '数据查询:所查询的实体不存在数据源中。', }, { re: /Cannot find name '__UnSupportedSqlFunction__'/, result: '数据查询:当前数据库不支持该Sql函数。', }, { re: /Cannot find name '__UnSupportedSqlFunctionAtCurrentClause__'/, result: '数据查询:当前使用范围不支持该Sql函数。', }, { re: /Cannot find name '__UnSupportedBuiltinFunctionInisdeCallQueryComponent__'/, result: '数据查询:不支持内置函数。', }, { re: /Cannot find name '__UnSupportedSqlFunctionOutsideCallQueryComponent__'/, result: 'SQL函数只能在数据查询中使用。', }, { re: /Cannot find name '__(?:WHERE_MULTIPLE_CONDITION)__'/, result: '数据查询:筛选条件不能有多个表达式。', }, { re: /Cannot find name '__(?:HAVING_MULTIPLE_CONDITION)__'/, result: '数据查询:聚合属性过滤不能有多个表达式。', }, { re: /Cannot find name '__(?:EMPTY_AGGREGATE_NAME)__'/, result: '数据查询:聚合函数名称不能为空。', }, { re: /Cannot find name '__(?:EMPTY_AGGREGATE_ALIAS)__'/, result: '数据查询:聚合函数别名不能为空。', }, { re: /Cannot find name '__(?:EMPTY_GROUP_BY_NAME)__'/, result: '数据查询:分组属性不能为空。', }, { re: /Cannot find name '__UnSupportedBuiltinFunctionInisdeCallQueryComponent__'/, result: '数据查询:不支持内置函数。', }, { re: /Cannot find name '__(?:EMPTY_GROUP_BY_ALIAS)__'/, result: '分组属性别名不能为空。', }, { re: /Cannot find name '__(?:SQL_GRAMMAR_ERROR)__'/, result: 'SQL查询:语句可能有异常。', }, { re: /Cannot find name '__(?:OQL_GRAMMAR_ERROR)__'/, result: 'SQL查询:语句可能有异常。', }, { re: /Cannot find name '__(?:OQL_ALIAS_WARNING)__'/, result: 'SQL查询:别名不符合规范。', }, { re: /Cannot find name '__(?:OQL_EMPTY_ERROR)__'/, result: 'SQL查询:语句不能为空。', }, { re: /Cannot find name '__OQL_UnSupportedSqlFunction__'/, result: 'SQL查询:当前数据库不支持该Sql函数。', }, { re: /Cannot find name '__OQL_UnSupportedSqlFunctionAtCurrentClause__'/, result: 'SQL查询:当前使用范围不支持该Sql函数。', }, { re: /Cannot find name '__(?:NO_SELECT_TYPE)__'/, result: '未选择返回数据类型', }, { re: /'_unsupported_syntax_lcap'/, result: 'SQL查询:不支持的使用方式', }, { re: /Cannot find name '__UsersEmpty__'./, result: '任务完成人为空', }, { re: /Cannot find name '__TaskTitleEmpty__'./, result: '任务标题为空', }, { re: /Cannot find name '__UserAssigneesEmpty__'./, result: '转派人员为空', }, { re: /Cannot find name '__RoleAssigneesEmpty__'./, result: '转派角色为空', }, { re: /Cannot find name '__DepartmentAssigneesEmpty__'./, result: '转派部门为空', }, { re: /Cannot find name '__specifiedRangeEmpty__'./, result: '回退节点范围为空', }, { re: /Cannot find name '__destinationEmpty__'./, result: '任务未关联页面', }, { re: /Cannot find name '__assignmentLineEmpty__'./, result: '批量赋值没有连线', }, { re: /Cannot find name '__ValidationRule__'./, result: '校验规则不能为空', }, { re: /Cannot find name '__UNCERTAIN__INTERMEDIATE__FIELDS__'./, result: '来自不确定结构或实体的字段', }, { re: /Cannot find name '__UNSUPPORTED__ENTITY__FIELDS__'./, result: '可能为实体属性别名,动态条件中仅支持使用变量', }, { re: /No value exists in scope for the shorthand property '__(?:IDENTIFIER|LEFT|RIGHT)__'. Either declare one or provide an initializer./, result: '用户任务未关联页面', }, { re: /No value exists in scope for the shorthand property '__(?:outOfProcess)__'. Either declare one or provide an initializer./, result: '节点未关联到流程图', }, { re: /'(.+?)' has no exported member(?: named)? '(.+?)'/, result: '找不到接口$1 $2。', transforms: [transformNamespace, transformType], }, { re: /Unreachable code detected\./, result: '逻辑中有未使用的。', }, // { // re: /Property '__slice' does not exist on type '(.+?)'/, // result: 'ForEach:循环列表:参数类型不匹配!所需类型:数组、集合等,提供类型:$1', // transforms: [transformType], // }, { re: /Property '(.+?)' does not exist on type 'typeof (.+?)'/, result: '找不到$2 $1。', transforms: [transformMethod, transformIdentifier], }, { re: /Property '(.+?)' does not exist on type '(.+?)'/, result: '找不到$2 $1。', transforms: [transformMethod, transformIdentifier], }, { re: /Cannot find name '(.+?)'. Did you mean '(.+?)'\?/, result: '找不到 $1。', transforms: [outOriginalvalue, outOriginalvalue], }, { re: /Expected (.+?) type arguments, but got (.+?)/, result: '预期 $1 个类型参数,但传入了 $2 个。', transforms: [outOriginalvalue, outOriginalvalue], }, { re: /Expected (.+?) arguments, but got (.+?)./, result: '预期 $1 个参数,但传入了 $2 个。', transforms: [outOriginalvalue, outOriginalvalue], }, { re: /The (.+?)-hand side of an arithmetic operation must be of type (.+?). type./, result: '表达式错误!', transforms: [outOriginalvalue, outOriginalvalue], }, { re: /Operator '(.+?)' cannot be applied to types '(.+?)' and '(.+?)'./, result: '符号:$1 参数类型不匹配!所需类型:基础类型>$2,提供类型:$3。', transforms: [outOriginalvalue, transformType, transformType], }, { re: /Object is possibly 'undefined'./, result: '所选择的内容未定义。', }, { re: /Cannot find name '(.+?)'./, result: '所选择的内容 $1 未定义。', transforms: [outOriginalvalue], }, { re: /Duplicate identifier '(.+?)'./, result: '当前内容中有重复的 $1,可能会影响使用。', transforms: [outOriginalvalue], }, { re: /'__LogicEmpty' is declared but its value is never read./, result: '逻辑为空,请编辑内容', }, { re: /'__destinationEmpty__' is declared but its value is never read./, result: '任务未关联页面', }, { re: /'__devConfigValueEmpty' is declared but its value is never read./, result: '配置参数开发环境取值未设置', }, { re: /'__onlineConfigValueEmpty' is declared but its value is never read./, result: '配置参数生产环境取值未设置', }, { re: /Variable '(.+?)' implicitly has an 'any' type./, result: '$1推导不出类型!请连线或手动设置', transforms: [transformType], }, { re: /'(.+?)' refers to a value, but is being used as a type here. Did you mean 'typeof (.+?)'?/, result: '$1未初始化!请连线', transforms: [transformType], }, { re: /Variable '(.+?)' is used before being assigned./, result: '变量 $1 在使用前未被初始化', transforms: [outOriginalvalue], }, { re: /Cannot assign to '(.+?)' because it is a read-only property./, result: '无法赋值只读属性 $1', transforms: [outOriginalvalue], }, ]; const POST_RULES = [ { re: /__IDENTIFIER__/g, result: 'undefined', }, { re: /Integer|"Integer"/g, result: '整数', }, { re: /Long|"Long"/g, result: '整数', }, { re: /Double|"Double"/g, result: '小数', }, { re: /Decimal|"Decimal"/g, result: '小数', }, { re: /String|"String"/g, result: '字符串', }, { re: /Boolean|"Boolean"/g, result: '布尔值', }, { re: /"READONLY\.[\w\.]+"/g, result: '不可赋值类型', }, { re: /unknown/g, result: '未知类型', }, { re: /never/g, result: '未知类型', }, // 匿名数据结构去掉冗余展示 __name: "AStructure_\w{8}" { re: /__name: "AStructure_\w{8}";/g, result: '', }, ]; Object.keys(NAMESPACE_CONCEPT_MAP).forEach((key) => { POST_RULES.push({ re: new RegExp(key, 'g'), result: NAMESPACE_CONCEPT_MAP[key], }); }); function translateDiagnosticMessage(message) { // 业务组件先处理 const businessComponent = /Namespace 'app.frontendTypes.(.+?).businessComponents' has no exported member '(.+?)'./.exec(message); if (businessComponent?.[1] && businessComponent?.[2]) { return `找不到${businessComponent?.[1]}端下的业务组件${businessComponent?.[2]}`; } for (const rule of TS_RULES) { const cap = rule.re.exec(message); if (cap) { return rule.result.replace(/\$(\d+)/g, (m, index) => rule.transforms[index - 1](cap[index])); } } return message; } exports.translateDiagnosticMessage = translateDiagnosticMessage; /** * 节点和ts 报错结合,转成更精准的ts报错 * @param minRange * @param tsErrorDetail 报错具体信息 * @returns */ function naslNodeTranslateMessage(minRange, tsErrorDetail, performance) { const text = tsErrorDetail?.originalDiagnostic?.text; let node = minRange?.node; if (performance) { if (node instanceof nasl_concepts_1.Destination && /Property '(.+?)' does not exist on type '(.+?)'/.exec(text)) { let nodePath = node.viewNamespace + '.' + node.viewName; function modifyViews(input = '') { const parts = input.split('.'); let firstViews = true; return parts .map((part) => { if (part === 'views') { if (firstViews) { firstViews = false; return part; } else { return 'children'; } } else { return part; } }) .join('.'); } nodePath = modifyViews(nodePath); const viewNode = node.app?.findNodeByCompleteName?.(nodePath); if (viewNode) { return null; } else { tsErrorDetail.message = `找不到页面 ${node.viewName}`; } } } // 有一些节点报错信息不向外暴露,缺失一些原生标签等等的展示 if (node instanceof nasl_concepts_1.View || node instanceof nasl_concepts_1.BusinessComponent || node instanceof nasl_concepts_1.ViewElement) { if (text.startsWith(`'nasl.ui' has no exported member named `)) { return null; } else if (text.startsWith(`Property '`) && text.includes(`does not exist on type 'typeof ui'.`)) { return null; } else if (text.startsWith(`Namespace 'nasl.ui' has no exported member `)) { return null; } else if (text.startsWith(`Function expression, which lacks return-type annotation, implicitly has an 'any[]' return type.`)) { return null; } else if (/Property 'businessComponents' does not exist on type 'typeof (pc|h5)'./.exec(text)) { return null; } } // 处理bindevent参数没传递的报错 if (/Expected (.+?) arguments, but got (.+?)./.exec(text)) { // bindevent本身现在就不传参数 if (node instanceof nasl_concepts_1.BindEvent) { return null; } if (node instanceof nasl_concepts_1.ProcessElement && (node.isUserTask || node.type === 'AutoTask' || node.type === 'StartNoneEvent') || node instanceof nasl_concepts_1.ProcessElementV2 && node.isTask) { const reg = /Expected (.+?) arguments, but got (.+?)./.exec(text); let u; if (node.concept === 'ProcessElementV2') { const map = { ServiceTask: '自动任务', InitiateTask: '发起任务', ApprovalTask: '审批任务', SubmitTask: '执行任务', CCTask: '抄送任务', }; u = map[node.type]; } else { switch (node.type) { case 'UserTask': u = '用户任务'; break; case 'AutoTask': u = '自动任务'; break; case 'ApprovalTask': u = '审批任务'; break; case 'MultiApprovalTask': u = '多人审批任务'; break; case 'InitiateTask': u = '发起任务'; break; case 'SubmitTask': u = '执行任务'; break; case 'CCTask': u = '抄送任务'; break; default: u = '任务'; } } if (reg[2] === '0') { tsErrorDetail.message = `${u}出口顺序流个数不能是0`; } else { tsErrorDetail.message = `${u}出口顺序流个数不能大于1`; } if (node.type === 'StartNoneEvent') { tsErrorDetail.message = `开始节点只能有一个出口顺序流`; } } if (node instanceof nasl_concepts_1.ProcessElement && ['ExclusiveGateway', 'ParallelGateway', 'InclusiveGateway'].includes(node.type) || node instanceof nasl_concepts_1.ProcessElementV2 && node.isBranch) { const reg = /Expected (.+?) arguments, but got (.+?)./.exec(text); let u = ''; let map; if (node.concept === 'ProcessElementV2') { map = { ExclusiveGateway: '唯一分支', ParallelGateway: '并行分支' }; } else { map = { ExclusiveGateway: '排他网关', ParallelGateway: '并行网关', InclusiveGateway: '包容网关', }; } u = map[node.type]; if (reg[2] === '0') { tsErrorDetail.message = `${u}出口顺序流和入口顺序流个数不能等于0`; } else { tsErrorDetail.message = node.type === 'ExclusiveGateway' ? `${u}出口顺序流个数等于1,该${u}没有意义` : `${u}出口顺序流和入口顺序流个数同时等于1,该${u}没有意义`; } } } // 因为缺少参数时候有可能有特殊情况,因为原本少的多了就会找到 callLogic上, // 但是只少一个的时候就会找到准确的Argument上 需要特殊处理, ui层就可以统一处理 if (/Expected (.+?) arguments, but got (.+?)./.exec(text) && node.getAncestor('CallLogic')) { node = node.getAncestor('CallLogic'); } // 赋值左右侧类型不一致, 把文字换一下 if (/Argument of type '(.+?)' is not assignable to parameter of type '(.+?)'/.exec(text)) { if (node instanceof nasl_concepts_1.OqlQueryComponent) { tsErrorDetail.message = tsErrorDetail.message .replace('参数类型不一致!传入类型:', '类型不一致!传入类型:') .replace('接收类型:', '期望类型:'); } else if (node.parentNode instanceof nasl_concepts_1.Assignment) { // OQL 的返回类型报错需要转换翻译,其内部 SQL 语句的参数类型报错不转换翻译 if (node instanceof nasl_concepts_1.OqlQueryComponent) { // @ts-expect-error FIXME wudengke node已经never了,不知道是在干什么 if (node.getCurrentSource().currentSource.start.line + 1 === tsErrorDetail.originalDiagnostic.start.line) { tsErrorDetail.message = tsErrorDetail.message .replace('参数类型不一致!传入类型:', '赋值:类型不一致!右边类型:') .replace('接收类型:', '左边类型:'); } } else { // node = node.getAncestor('CallLogic'); tsErrorDetail.message = tsErrorDetail.message .replace('参数类型不一致!传入类型:', '赋值:类型不一致!右边类型:') .replace('接收类型:', '左边类型:'); } } else if (node.parentNode instanceof nasl_concepts_1.NewMap) { if (text.includes(`is not assignable to parameter of type 'None'`)) { const cap = /Argument of type '(.+?)' is not assignable to parameter of type '(.+?)'/.exec(text); tsErrorDetail.message = tsErrorDetail.message = `参数类型不一致!传入类型:${transformType(cap[1])},接收类型:字符串 或 布尔值 或 整数 或 小数`; } else if (text.includes(`parameter of type 'never'`)) { tsErrorDetail.message = '自动推导不出类型!newMap中有未知类型'; } } else if (node.parentNode instanceof nasl_concepts_1.NewList && text.includes(`parameter of type 'never'`)) { tsErrorDetail.message = '自动推导不出类型!newList中有未知类型'; } else if (node instanceof nasl_concepts_1.ProcessOutcome || node instanceof nasl_concepts_1.ProcessOutcomes) { const cap = /Argument of type '(.+?)' is not assignable to parameter of type '(.+?)'/.exec(text); const selectComes = cap[1].split(' | '); const currentComes = cap[2].split(' | '); if (node instanceof nasl_concepts_1.ProcessOutcome) { tsErrorDetail.message = `流向出口不存在,${selectComes.map((item) => item).join(',')}出口流不属于[${currentComes.map((item) => item).join(',')}]当中的某个流`; } else { tsErrorDetail.message = `流向出口不存在,${selectComes.map((item) => item).join(',')}有错误的出口流`; } } else if (node.parentNode instanceof nasl_concepts_1.AssigneeV2) { const cap = /Argument of type '(.+?)' is not assignable to parameter of type '(.+?)'/.exec(text); tsErrorDetail.message = `审批人传入类型错误,传入类型:${transformType(cap[1])},接收类型:${transformType(cap[2])}`; } } if (/Property '__slice' does not exist on type '(.+?)'/.exec(text)) { const cap = /Property '__slice' does not exist on type '(.+?)'/.exec(text); if (cap) { if (node instanceof nasl_concepts_1.ForEachStatement) { tsErrorDetail.message = `ForEach:循环列表:参数类型不匹配!所需类型:数组、集合等,提供类型:$1`.replace(/\$(\d+)/g, (m, index) => transformType(cap[index])); } else if (node instanceof nasl_concepts_1.Paginate) { tsErrorDetail.message = `分页:分页对象类型不匹配!所需类型:数组、集合等,提供类型:$1`.replace(/\$(\d+)/g, (m, index) => transformType(cap[index])); } } } if (node instanceof nasl_concepts_1.Argument || node instanceof nasl_concepts_1.Anchor) { // if (/Type '(.+?)' is not assignable to type '(.+?)'./.exec(text)) { // } // Argument在callInterface上的报错放到内部 if (node.expression) { node = node.expression; } } if (node instanceof nasl_concepts_1.AnonymousFunction) { if (node.body) node = node.body; } else if (node instanceof nasl_concepts_1.Function) { if (node.returnExpression) node = node.returnExpression; } // 处理一些复制过来的内容 Identifier或者MemberExpression内容缺失,错误要像上冒 if (node instanceof nasl_concepts_1.Identifier || node instanceof nasl_concepts_1.MemberExpression) { if (node.parentNode instanceof nasl_concepts_1.MemberExpression) { let parentNode = node; while (parentNode && parentNode.parentNode instanceof nasl_concepts_1.MemberExpression) { parentNode = parentNode.parentNode; } node = parentNode || node; let str = tsErrorDetail.message; str = node.constructor.nodeTitle + ':' + str; tsErrorDetail.message = str; } } // 类型报错的,需要放到上一级 if (/'(.+?)' has no exported member(?: named)? '(.+?)'/.exec(text) || /Property '(.+?)' does not exist on type '(.+?)'./.exec(text)) { if (node instanceof nasl_concepts_1.TypeAnnotation) { const { preferenceMap } = node.app; // 元数据需要单独判断 if (String(preferenceMap.metadataTypeEnable) === 'false' && node.typeNamespace === 'app.metadataTypes' && node.typeKind === 'reference') { tsErrorDetail.message = '未启用元数据功能,请在“偏好设置”中启用元数据。'; } // 变量 type 的报错放到 type 本身上 else { tsErrorDetail.message = node.parentNode.constructor.nodeTitle + ':' + tsErrorDetail.message; } } } if (text === "Cannot find namespace 'sharedApp'." || /Namespace 'sharedApp\.[^']+' has no exported member/.test(text)) { if (node instanceof nasl_concepts_1.TypeAnnotation) { const { typeNamespace, typeName } = node; const segs = typeNamespace?.split('.') || []; if (segs[0] === 'sharedApp') { const typeMap = { 'entities': '实体', 'structures': '数据结构', 'enums': '枚举', 'metadataTypes': '元数据', }; const type = typeMap[segs.pop()]; tsErrorDetail.message = `找不到共享数据 ${segs[1]} ${type} ${typeName}`; } } } // 实体只有只读权限 "Property 'delete' does not exist on type '{ get(id: Long): EntityShareReadOnly; }' if (text === "Cannot find name 'sharedApp'." || /Property '[^']+' does not exist on type '[^']+'\./.test(text)) { if (node instanceof nasl_concepts_1.CallLogic) { const { calleeNamespace, calleeName } = node; const segs = calleeNamespace?.split('.') || []; if (segs[0] === 'sharedApp') { // 实体逻辑 // calleeNamespace: "sharedApp.appshare.dataSources.defaultDS.entities.EntityShareSimple.logics" // calleeName: "delete" const match = /\.entities\.(.+?)\.logics$/.exec(calleeNamespace); const entityName = match && match[1]; tsErrorDetail.message = entityName ? `找不到共享数据 ${segs[1]} 实体 ${entityName} 逻辑 ${calleeName}` : `找不到共享数据 ${segs[1]} 逻辑 ${calleeName}`; } } if (node instanceof nasl_concepts_1.QueryFieldExpression) { const entityCompleteName = node.entityCompleteName; const segs = entityCompleteName?.split('.') || []; const entityAsName = node.entityAsName; if (segs[0] === 'sharedApp') { tsErrorDetail.message = `找不到共享数据 ${segs[1]} 实体 ${entityAsName}`; } } } // 共享逻辑不用报错,共享逻辑的报错信息在逻辑签名(入参和出参)上 if (node instanceof nasl_concepts_1.Logic && node.module?.isSharedApp) { return null; } if (node instanceof nasl_concepts_1.OqlQueryComponent && text === "Type instantiation is excessively deep and possibly infinite.") { return null; } /** * 所有问题的报错处理具体信息 * 转成一个最合理的文案 */ if (/^Cannot find name '__(?:IDENTIFIER|LEFT|RIGHT)__'\.$/.exec(text)) { if (node instanceof nasl_concepts_1.IfStatement || node instanceof nasl_concepts_1.SwitchCase || node instanceof nasl_concepts_1.WhileStatement) { tsErrorDetail.message = node.constructor.nodeTitle + ':参数不能为空!所需类型:基础类型>布尔值'; } else if (node instanceof nasl_concepts_1.ForEachStatement) { tsErrorDetail.message = 'ForEach:结束值:不能为空!所需类型:基础类型>整数'; } else if (node instanceof nasl_concepts_1.UnaryExpression) { tsErrorDetail.message = '!:参数不能为空!'; } else if (node instanceof nasl_concepts_1.Assignment) { tsErrorDetail.message = '赋值:参数不能为空!'; } else if (node instanceof nasl_concepts_1.BatchAssignment) { tsErrorDetail.message = '批量赋值:参数不能为空'; } else if ((node instanceof nasl_concepts_1.Argument && node.parentNode instanceof nasl_concepts_1.CallInterface) || node instanceof nasl_concepts_1.CallInterface) { tsErrorDetail.message = '调用接口:参数不能为空!'; } else if ((node instanceof nasl_concepts_1.Argument && node.parentNode instanceof nasl_concepts_1.CallFunction) || node instanceof nasl_concepts_1.CallFunction) { tsErrorDetail.message = '调用函数:参数不能为空!'; } else if ((node instanceof nasl_concepts_1.Argument && node.parentNode instanceof nasl_concepts_1.CallLogic) || node instanceof nasl_concepts_1.CallLogic) { tsErrorDetail.message = '调用逻辑:参数不能为空!'; } else if (node instanceof nasl_concepts_1.ProcessOutcome || node instanceof nasl_concepts_1.Function || node instanceof nasl_concepts_1.MatchCase) { tsErrorDetail.message = node.constructor.nodeTitle + ':不能为空!'; } else if (node instanceof nasl_concepts_1.NewMap) { tsErrorDetail.message = 'Map推导不出类型!请手动设置!'; } else if (node instanceof nasl_concepts_1.NewList) { tsErrorDetail.message = 'List推导不出类型!请手动设置!'; } else if (node instanceof nasl_concepts_1.Argument) { let str = tsErrorDetail.message; str = node.parentNode.constructor.nodeTitle + ':' + str; tsErrorDetail.message = str; } else if (node instanceof nasl_concepts_1.TypeAnnotation) { // 如果是TypeAnnotation 嵌套TypeAnnotation,就往上冒,多是那种T嵌套的 let str = tsErrorDetail.message; // TypeAnnotation要冒泡到最上面 while (node.parentNode && node.parentNode instanceof nasl_concepts_1.TypeAnnotation) { node = node.parentNode; } str = node.parentNode.constructor.nodeTitle + ':' + str; tsErrorDetail.message = str; } else if (node instanceof nasl_concepts_1.Identifier) { // 根据父级内容推断当前缺少啥 const identifierToString = (node) => { let str = tsErrorDetail.message; if (node.parentNode instanceof nasl_concepts_1.Assignment) { str = '赋值:参数不能为空!'; } else if (node.parentNode instanceof nasl_concepts_1.Argument && node.parentNode.parentNode instanceof nasl_concepts_1.CallLogic) { str = '调用逻辑:参数不能为空!'; } else if (node.parentNode instanceof nasl_concepts_1.ValidationRule) { str = '验证逻辑:验证对象不能为空!'; } else { str = node.parentNode.constructor.nodeTitle + ':' + str; tsErrorDetail.message = str; } return str; }; tsErrorDetail.message = identifierToString(node); } } if (/'__UpdateNoProperty__' is declared but its value is never read./.exec(text)) { tsErrorDetail.message = '局部更新:未选择任何属性'; const callLogic = node?.parentNode?.parentNode?.parentNode; const bodyArgument = callLogic?.arguments?.find((item) => item.keyword === 'body'); node = bodyArgument?.expression; } // 左右类型不一致的,把上级信息带上 if (/^Type '(.+?)' is not assignable to type '(.+?)'\./.exec(text) || /^This condition will always return '(.+?)' since the types '(.+?)' and '(.+?)' have no overlap./.exec(text)) { if (node instanceof nasl_concepts_1.OqlQueryComponent) { // SQL语句:查询字段重复:id let cap = /^Type '(.+?)' is not assignable to type 'true'\./.exec(text); if (cap) { const errorMsg = 'SQL查询:查询字段重复:$1'.replace(/\$(\d+)/g, (m, index) => transformType(cap[index])); tsErrorDetail.message = errorMsg; } else { cap = /^Type '(.+?)' is not assignable to type '(.+?)'\./.exec(text); if (cap) { const errorMsg = 'SQL查询:返回类型不一致:选择类型:$2,返回类型:$1'.replace(/\$(\d+)/g, (m, index) => transformType(cap[index])); tsErrorDetail.message = errorMsg; } } } else { let str = tsErrorDetail.message; str = node.parentNode.constructor.nodeTitle + ':' + str; tsErrorDetail.message = str; if (node.parentNode instanceof nasl_concepts_1.AnonymousFunction) { // 匿名函数:左右参数类型不一致!左边类型:布尔值,右边类型:整数。 tsErrorDetail.message = tsErrorDetail.message.replace('左右参数', '返回值').replace('左边类型', '接收类型').replace('右边类型', '传入类型'); } else if (text === `Type 'void' is not assignable to type 'never'.`) { const callLogic = node.getAncestor('CallLogic'); const arg = node.getAncestor('Argument'); if (callLogic?.isSpecificProcessLogic && arg?.keyword === 'data') { tsErrorDetail.message = `${node.name}不能为null`; } else { tsErrorDetail.message = `${node.name}推导不出类型!请连线或手动设置`; } } } } if (/Property '(.+?)' is missing in type '(.+?)' but required in type '(.+?)'/.exec(text)) { if (node.parentNode instanceof nasl_concepts_1.NewComposite) { tsErrorDetail.message = tsErrorDetail.message .replace('类型不一致', '接收与传入类型不一致') .replace('左边类型', '左边接收类型') .replace('右边类型', '右边传入类型'); tsErrorDetail.message = node.name + tsErrorDetail.message; } } // 参数类型不一致! if (/^Type '(.+?)' does not satisfy the constraint '(.+?)'\./.exec(text)) { // match if ((node instanceof nasl_concepts_1.TypeAnnotation && node.parentNode instanceof nasl_concepts_1.MatchCase) || node instanceof nasl_concepts_1.MatchCase) { return null; // ide进行计算 // const reg = /^Type '(.+?)' does not satisfy the constraint '(.+?)'\./.exec(text); // if (reg[1] && reg[2]) { // tsErrorDetail.message = '匹配:类型不一致! 传入类型:' + transformType(reg[1]) + ',接收类型:' + transformType(reg[2]); // if (reg[2] === 'never') { // tsErrorDetail.message = '匹配:类型不一致! 传入类型:' + transformType(reg[1]) + ',接收类型:无可以接收的类型'; // } // } // tsErrorDetail.titleTip = '该类型无法满足条件'; } } // 左右类型不一致的,把上级信息带上 if (/^A type literal property cannot have an initializer./.exec(text)) { if (node instanceof nasl_concepts_1.Param) return null; } if (/'__LogicEmpty' is declared but its value is never read./.exec(text)) { if (node instanceof nasl_concepts_1.Logic) { node.isSmpty = true; } } // FIXME 自定义组件库使用了slotXX(current: 自定义类型)。但是有些时候这里不会翻译出__item。因此先暂时写规则忽略。 // 缺陷:vue3日历拖入后有报错 http://projectmanage.netease-official.lcap.163yun.com/dashboard/BugDetail?id=3027550191498752 if (text?.includes("Cannot find name '__item'.")) { return null; } if (/Variable '(.+?)' implicitly has an 'any' type./.exec(text) || /Variable '(.+?)' implicitly has an 'any\[\]' type./.exec(text)) { if (node.parentNode instanceof nasl_concepts_1.NewComposite) { // } else if (node.parentNode instanceof nasl_concepts_1.NewList) { tsErrorDetail.message = tsErrorDetail.message.replace('!请连线或手动设置', ''); } else if (node.parentNode instanceof nasl_concepts_1.NewMap) { tsErrorDetail.message = tsErrorDetail.message.replace('!请连线或手动设置', ''); } else { return; } } if (checkAStructure_(text)) { return null; } for (const rule of POST_RULES) { if (rule.re.test(tsErrorDetail.message)) { tsErrorDetail.message = tsErrorDetail.message.replace(rule.re, (value, index, oldStr) => { // 处理匹配到的值的左右的内容,是不是还是字母,要是字母就原样输出,防止展示问题 try { const leftCode = index - 1 < 0 ? '' : oldStr[index - 1]; const rightCode = oldStr[index + value.length] || ''; if (/^[A-Za-z]+$/.test(leftCode) || /^[A-Za-z]+$/.test(rightCode)) { return value; } else { return rule.result; } } catch (err) { return value; } }); } } if (node instanceof nasl_concepts_1.BindAttribute && node.parentNode instanceof nasl_concepts_1.ViewElement) { const reg = /左边类型:\s*(.*)[,,]\s*右边类型:\s*(.*)[.。]/; const left = reg.exec(tsErrorDetail.message)?.[1]; const right = reg.exec(tsErrorDetail.message)?.[2]; if (left && right) { tsErrorDetail.message = `页面组件:属性类型不一致!接收类型:${left},传入类型:${right}。`; } } if (node instanceof nasl_concepts_1.MemberExpression && node.isViewElementProperty) { // setProp的报错信息 const isAssignment = node.upperNode?.concept === 'Assignment' && node.upperNode?.left === node; const isBatchAssignment = node.upperNode?.concept === 'SelectMembers' && node.upperNode?.upperNode?.concept === 'BatchAssignment' && node.upperNode?.upperNode?.left === node.upperNode; if (isAssignment || isBatchAssignment) { // 找不到 UTableViewOptions<未知类型, 未知类型, 布尔值, 布尔值> 上的 page。 const reg = /找不到\s*(.*)\s*上的\s*(.*)[.。]/; const prop = reg.exec(tsErrorDetail.message)?.[2]; tsErrorDetail.message = `组件属性赋值:属性名${prop}不存在!`; if (/Options$/.test(prop)) { tsErrorDetail.message = `组件属性赋值:找不到组件${prop.replace('Options', '')}`; } } else { // getState的报错信息 } } if (node instanceof nasl_concepts_1.Assignment || node instanceof nasl_concepts_1.BatchAssignment) { if (tsErrorDetail.message.indexOf('__SET_PROP_RISK__') !== -1) { tsErrorDetail.message = '组件属性赋值:属性已开启动态绑定,不支持赋值操作'; } } if (node instanceof nasl_concepts_1.Identifier) { if (tsErrorDetail.message.indexOf('__INVALID_ELEMENT_IDENTIFIER__') !== -1) { tsErrorDetail.message = '暂不支持组件当作变量使用'; } } if (node instanceof nasl_concepts_1.Dependency) { const connector = node.getAncestor('Connector'); tsErrorDetail.message = `连接器 ${connector.title} 需要依赖 版本至少为${node.version}${node.refName}`; } if ((0, asserts_1.isMicroserviceInterface)(node) || (0, asserts_1.isCallMicroserviceInterface)(node)) { const reg = /Property '(.+?)' does not exist on type 'typeof connections'./.exec(text); // 兼容当最后一个连接被删掉之后,报错信息不准确的问题 const reg2 = /Property 'connections' does not exist on type 'typeof app'./.exec(text); if (reg) { tsErrorDetail.message = `找不到 微服务连接器 上的 ${reg[1]}。`; } else if (reg2) { tsErrorDetail.message = `找不到 相关微服务连接器。`; } } // 特殊处理errorType的写入报错 if (tsErrorDetail.message && tsErrorDetail.message.includes('不可赋值类型 & 字符串')) { tsErrorDetail.message = tsErrorDetail.message.replaceAll('不可赋值类型 & 字符串', '不可赋值类型'); } if (node && tsErrorDetail) { // node.tsErrorDetail = tsErrorDetail; } // 如果是来自于 编辑态的自定义连接器 的节点,报错信息需要特殊处理 const isFromConnector = node?.getAncestor('Connector'); const isFromIntegration = node?.getAncestor('Integration'); if (isFromConnector && isFromIntegration) { return getConnectorErrorDetail(node, tsErrorDetail); } const isListSortNode = node?.getAncestor('CallFunction'); if (isListSortNode instanceof nasl_concepts_1.CallFunction && isListSortNode?.calleeName === 'ListSort') { if (tsErrorDetail.message?.includes('by推导不出类型!')) { tsErrorDetail.message = 'by 推导不出类型!请修改或设置内容'; } if (tsErrorDetail.message?.includes('参数类型不一致!传入类型:')) { const reg = /by:\s*([^;]+)/g; const byTypes = tsErrorDetail.message.match(reg); if (byTypes && byTypes?.length >= 2) { const [receivedType, expectedType] = byTypes.map(type => type.split(':')[1].trim()); tsErrorDetail.message = `参数类型不一致!传入类型:${receivedType},接收类型:${expectedType}`; } } } return { node, ...tsErrorDetail, }; } exports.naslNodeTranslateMessage = naslNodeTranslateMessage; /** * 获取自定义连接器的报错信息 * @param node * @param tsErrorDetail */ function getConnectorErrorDetail(node, tsErrorDetail) { // 如果message 为纯英文,直接返回 if (/^[A-Za-z\s]+[.,!?;:]$/.test(tsErrorDetail.message)) { return { node, ...tsErrorDetail, }; } const connector = node.getAncestor('Connector'); let targetNode = node; let msgPrefix = ''; const authLogic = node.getAncestor('AuthLogic'); const authLogicForCallInterface = node.getAncestor('AuthLogicForCallInterface'); const connectorLogic = node.getAncestor('ConnectorLogic'); if (authLogicForCallInterface) { targetNode = authLogicForCallInterface; msgPrefix = `${connector.title} 连接器 ${targetNode.name} 操作鉴权方式:`; // 连接器中的鉴权方法中,不允许调用 已经鉴权的接口 if (nasl_concepts_1.asserts.isCallAuthInterface(node)) { tsErrorDetail.message = `不支持鉴权接口调用,但调用接口 ${node.calleeName} 为鉴权接口调用`; } } else if (authLogic) { targetNode = authLogic; msgPrefix = `${connector.title} 连接器 ${targetNode?.name} 触发鉴权方式:`; // 连接器中的鉴权方法中,不允许调用 已经鉴权的接口 if (nasl_concepts_1.asserts.isCallAuthInterface(node)) { tsErrorDetail.message = `不支持鉴权接口调用,但调用接口 ${node.calleeName} 为鉴权接口调用`; } } else if (connectorLogic) { targetNode = connectorLogic; const namespace = node.getAncestor('Namespace'); msgPrefix = `连接器(${connector.title})分组(${namespace.title}) 操作(${targetNode?.title}) 中:`; } else if (nasl_concepts_1.asserts.isConnectorLogic(node)) { targetNode = node; const namespace = node.getAncestor('Namespace'); msgPrefix = `连接器(${connector.title})分组(${namespace.title}) 操作(${targetNode?.title}) 中:`; } else if (targetNode.name) { // 本身就是 AuthLogic、AuthLogicForCallInterface msgPrefix = `${connector.title} 连接器 ${targetNode.name} 操作中:`; } else { msgPrefix = `连接器(${connector.title})中:`; } tsErrorDetail.message = msgPrefix + tsErrorDetail.message; return { node, ...tsErrorDetail, }; } function checkAStructure_(errorText) { const index = TS_RULES_TYPE_INCONSISTENCY.findIndex((item) => item.exec(errorText)); if (index !== -1) { const rule = TS_RULES_TYPE_INCONSISTENCY[index]; const cap = rule.exec(errorText); if (rule === TS_RULES_TYPE_INCONSISTENCY[0] || rule === TS_RULES_TYPE_INCONSISTENCY[1]) { cap.splice(1, 1); } if (cap[1].includes('__name: "AStructure_') && cap[2].includes('__name: "AStructure_')) { const x = (0, sortTsString_1.sortTsString)(transformType(cap[1])); const y = (0, sortTsString_1.sortTsString)(transformType(cap[2])); if (x === null || y === null) { return; } if (x === y) { return true; } else if (mapAstString.get(x) === y || mapAstString.get(y) === x) { return true; } else { const regex = /Type '"AStructure_(.*)' is not assignable to type '"AStructure_(.*)'/; const nameError = regex.exec(errorText); const length1 = x.match(/:/g) ? x.match(/:/g).length : -1; const length2 = y.match(/:/g) ? y.match(/:/g).length : -2; if (nameError && length1 === length2) { mapAstString.set(x, y); mapAstString.set(y, x); return true; } } } } return false; } exports.checkAStructure_ = checkAStructure_; //# sourceMappingURL=translator.js.map