UNPKG

@lcap/nasl

Version:

NetEase Application Specific Language

484 lines 25.1 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.parseNaturalTS = exports.addType2Namespace = void 0; const babel = __importStar(require("@babel/core")); const utils_1 = require("./transforms/utils"); const transform2Entity_1 = require("./transforms/transform2Entity"); const transform2Structure_1 = require("./transforms/transform2Structure"); const transform2GlobalLogicDeclaration_1 = require("./transforms/transform2GlobalLogicDeclaration"); const transform2Enum_1 = require("./transforms/transform2Enum"); const transform2Logic_1 = require("./transforms/transform2Logic"); const transform2Variable_1 = require("./transforms/transform2Variable"); const parseNaturalTSXView_1 = require("./parseNaturalTSXView"); const transform2MetadataType_1 = require("./transforms/transform2MetadataType"); const transformThemeAndStyle_1 = require("./transforms/transformThemeAndStyle"); /** * 铺平命名空间 * 会将连续嵌套的命令空间铺平 */ function flatNamespace(node) { const sub = node.body; if (sub.type === 'TSModuleDeclaration') { const { ids, block } = flatNamespace(sub); return { ids: [node.id, ...ids], block }; } else return { ids: [node.id], block: sub }; } /** * 将 Statements 铺平成如下的形式: * ids: extensions.mylib.enums statement: ClassDeclaration * ids: extensions.mylib.structures statement: ClassDeclaration * ids: extensions.mylib.logics statement: TSDeclareFunction * ids: extensions.mylib.logics statement: FunctionDeclaration * ids: apis.myInterface.enums statement: ClassDeclaration * ids: apis.myInterface.structures statement: ClassDeclaration * ids: apis.myInterface.interfaces statement: TSDeclareFunction * ids: apis.myInterface.interfaces statement: FunctionDeclaration */ function flatMapNamespaceStatements(nodes, isInner = false, parseState) { const result = []; nodes.forEach((node) => { let statement = node; if (node.type === 'ExportNamedDeclaration') { // if (!isInner) return throwError(options?.parsingId, '顶层不应该导出:' + node.declaration.type); statement = node.declaration; statement.leadingComments = node.leadingComments; } else { if (isInner && !(node.type === 'ExpressionStatement' && ['CallExpression', 'TaggedTemplateExpression'].includes(node.expression.type))) // && node.expression.callee.type === 'Identifier' && /^[@$]/.test(node.expression.callee.name))) return (0, utils_1.throwError)(parseState, '必须使用 export', node.type, node); } if (statement.type === 'TSModuleDeclaration') { const curr = flatNamespace(statement); const nodeNamespace = curr.ids.map((id) => id.name).join('.'); console.log('nodeNamespace in flatMapNamespaceStatements:', nodeNamespace); const subResult = flatMapNamespaceStatements(curr.block.body, true, { ...parseState, nodeNamespace }); subResult.forEach((item) => { result.push({ ids: [...curr.ids, ...item.ids], statement: item.statement }); }); } else { result.push({ ids: [], statement }); } }); return result; } function handleDependency({ dependencies, moduleType, listName, opts, node }) { let dependency = dependencies.find((d) => d.name === opts?.moduleName); if (!dependency) { if (moduleType === 'connector') { dependency = new utils_1.naslTypes.Connector({ type: moduleType, name: opts.moduleName, }); } else { dependency = new utils_1.naslTypes.Module({ type: moduleType, name: opts.moduleName, }); } dependencies.push(dependency); } if (listName === 'structures' || listName === 'enums') { // 枚举、数据结构 dependency[listName].push(node); } else { let logicList = moduleType === 'extension' || moduleType === 'connector' ? dependency.logics : dependency.interfaces; if (moduleType === 'connector') { if (!TYPE_IN_NAMESPACES.includes(listName)) { let ns = dependency.namespaces.find((_ns) => _ns.name === listName); if (!ns) { ns = new utils_1.naslTypes.Namespace({ name: listName, title: listName, }); dependency.namespaces.push(ns); } logicList = ns.logics; } } const hasLogicIndex = logicList.findIndex((item) => item.name === node.name); if (opts.logicKind === 'declare') { // 逻辑不存在时添加 if (hasLogicIndex === -1) { logicList.push(node); } } else if (opts.logicKind === 'export') { if (hasLogicIndex !== -1) { // 覆盖 logicList[hasLogicIndex] = node; } else { // 添加 logicList.push(node); } } } } const HEAD_NAMESPACES = ['app', 'extensions', 'apis', 'connectors']; const TYPE_IN_NAMESPACES = ['structures', 'enums', 'metadataTypes', 'logics', 'interfaces']; /** * 添加到指定命名空间中 */ function addType2Namespace(app, node, namespacePrefix, type, opts) { if (namespacePrefix === 'app') { app[type].push(node); } if (namespacePrefix === 'extensions') { handleDependency({ dependencies: app.dependencies, moduleType: 'extension', listName: type, opts, node }); } else if (namespacePrefix === 'connectors') { handleDependency({ dependencies: app.connectorDependencies, moduleType: 'connector', listName: type, opts, node }); } else if (namespacePrefix === 'apis') { if (type === 'interfaces') { // Interface 特殊处理一下 const interfaceParams = node.params.map((item) => { const name = item?.name?.replace('_r_', '-'); return new utils_1.naslTypes.InterfaceParam({ ...item.toJSON(), name, in: 'body', required: !!item?.required, }); }); node = new utils_1.naslTypes.Interface({ ...node.toJSON(), title: node.name, path: 'path', method: "POST", protocol: "HTTPS", params: interfaceParams }); } handleDependency({ dependencies: app.interfaceDependencies, moduleType: 'interface', listName: type, opts, node }); } } exports.addType2Namespace = addType2Namespace; function addConnectionByName(app, connectionName, connectorName) { app.addConnection(new utils_1.naslTypes.Connection({ name: connectionName, namespace: `connector.${connectorName}`, })); } function parseNaturalTS(tsCode, options) { const plugins = [ [require('@babel/plugin-proposal-decorators'), { legacy: true }], require('@babel/plugin-proposal-class-properties'), ]; const root = babel.parseSync(tsCode, { filename: 'result.tsx', presets: [require('@babel/preset-typescript')], plugins }); const app = new utils_1.naslTypes.App(); const namespaceStatements = flatMapNamespaceStatements(root.program.body, false, { $$beacon: true, parsingId: options?.parsingId }); let viewCacheMap = {}; namespaceStatements.forEach(({ ids: _ids, statement }, index) => { const idNames = _ids.map((id) => id.name); const idNamesStr = idNames.join('.'); console.log('nodeNamespace in namespaceStatements:', idNamesStr); const parseState = { $$beacon: true, parsingId: options?.parsingId, nodeNamespace: idNamesStr, nodeName: '', appendExtName: true, }; if (!HEAD_NAMESPACES.includes(idNames[0])) return (0, utils_1.throwError)(parseState, `不支持命名空间以${idNames[0]}开头`); let headNamespace = idNames[0]; let typeInNamespace; if (headNamespace === 'app') { typeInNamespace = idNames[1]; } else { if (!idNames[2]) { if (statement.type === 'TSDeclareFunction' && statement.id?.name === 'connect') { const paramType = (statement.params[0]?.typeAnnotation).typeAnnotation; if (paramType.type === 'TSLiteralType' && paramType.literal.type === 'StringLiteral') { const connectionName = paramType.literal.value; addConnectionByName(app, connectionName, idNames[1]); return; } else if (paramType.type !== 'TSUnionType') return (0, utils_1.throwError)(parseState, '连接必须使用字符串联合类型'); else { const connectionNames = paramType.types.map((type) => { if (type.type === 'TSLiteralType' && type.literal.type === 'StringLiteral') { return type.literal.value; } else { return (0, utils_1.throwError)(parseState, '连接必须使用字符串字面量'); } }).filter(Boolean); connectionNames.forEach((connectionName) => { addConnectionByName(app, connectionName, idNames[1]); }); return; } } else { return (0, utils_1.throwError)(parseState, `不支持的命名空间:${idNamesStr}`); } } else { typeInNamespace = idNames[2]; } } let tailNamespace = idNames[idNames.length - 1]; parseState.moduleName = headNamespace === 'app' ? '' : idNames[1]; if (idNames[1] === 'dataSources' && idNames[3] === 'entities') { let dataSource = app.dataSources[0]; if (dataSource) { if (dataSource.name !== idNames[2]) return (0, utils_1.throwError)(parseState, `暂时不支持多数据源${dataSource.name},${idNames[2]}`); } else { dataSource = new utils_1.naslTypes.DataSource({ name: idNames[2], }); app.dataSources.push(dataSource); } if (statement.type === 'ClassDeclaration') { dataSource.entities.push((0, transform2Entity_1.transform2Entity)(statement, { ...parseState })); } else if (statement.type === 'VariableDeclaration') { } else if (statement.type === 'ExpressionStatement' && statement.expression.type === 'CallExpression' && statement.expression.callee.type === 'MemberExpression' && statement.expression.callee.property.type === 'Identifier' && statement.expression.callee.property.name === '$mockData') { if (statement.expression.arguments.length > 0) { const firstArg = statement.expression.arguments[0]; try { const argCode = (0, utils_1.generate)(firstArg).code; JSON.parse(argCode); } catch (error) { return (0, utils_1.throwError)({ ...parseState, nodeName: dataSource.entities[dataSource.entities.length - 1]?.name }, '$mockData的参数不是有效的JSON格式', firstArg.type, statement); } } } else return (0, utils_1.throwError)(parseState, '实体命名空间中不支持的节点类型', statement.type, statement); } else if (typeInNamespace === 'structures') { if (statement.type === 'ClassDeclaration') { addType2Namespace(app, (0, transform2Structure_1.transform2Structure)(statement, { ...parseState }), headNamespace, typeInNamespace, { ...parseState }); } else return (0, utils_1.throwError)(parseState, '数据结构命名空间中不支持的节点类型', statement.type, statement); } else if (typeInNamespace === 'enums') { if (statement.type === 'ClassDeclaration') { addType2Namespace(app, (0, transform2Enum_1.transform2Enum)(statement, statement.leadingComments, { ...parseState }), headNamespace, typeInNamespace, { ...parseState }); } else return (0, utils_1.throwError)(parseState, '枚举命名空间中不支持的节点类型', statement.type, statement); } else if (typeInNamespace === 'metadataTypes') { if (statement.type === 'TSTypeAliasDeclaration') { addType2Namespace(app, (0, transform2MetadataType_1.transform2MetadataType)(statement, statement.leadingComments, { ...parseState }), headNamespace, typeInNamespace, { ...parseState }); } else return (0, utils_1.throwError)(parseState, '元数据类型命名空间中不支持的节点类型', statement.type, statement); } else if (typeInNamespace === 'logics') { const logicParseState = { ...parseState, logicKind: statement.type === 'TSDeclareFunction' ? 'declare' : 'export' }; if (statement.type === 'TSDeclareFunction') { addType2Namespace(app, (0, transform2GlobalLogicDeclaration_1.transform2GlobalLogicDeclaration)(statement, { ...parseState }), headNamespace, typeInNamespace, logicParseState); } else if (statement.type === 'FunctionDeclaration') { addType2Namespace(app, (0, transform2Logic_1.transform2Logic)(statement, undefined, { ...parseState }), headNamespace, typeInNamespace, logicParseState); } else return (0, utils_1.throwError)(parseState, '逻辑命名空间中不支持的节点类型', statement.type, statement); } else if (['apis', 'extensions', 'connectors'].includes(headNamespace)) { // logics / interfaces if (tailNamespace === 'logics' || tailNamespace === 'interfaces') { const logicParseState = { ...parseState, logicKind: statement.type === 'TSDeclareFunction' ? 'declare' : 'export' }; const logic = (0, transform2Logic_1.transform2Logic)(statement, undefined, logicParseState); if (logicParseState.logicKind === 'declare') { logic.body = []; } addType2Namespace(app, logic, headNamespace, tailNamespace, logicParseState); } else if (tailNamespace === 'enums' && statement.type === 'ClassDeclaration') { addType2Namespace(app, (0, transform2Enum_1.transform2Enum)(statement, statement.leadingComments, { ...parseState }), headNamespace, tailNamespace, parseState); } else if (tailNamespace === 'structures' && statement.type === 'ClassDeclaration') { addType2Namespace(app, (0, transform2Structure_1.transform2Structure)(statement, { ...parseState }), headNamespace, tailNamespace, parseState); } else if (headNamespace === 'connectors') { const logicParseState = { ...parseState, logicKind: statement.type === 'TSDeclareFunction' ? 'declare' : 'export' }; const logic = (0, transform2Logic_1.transform2Logic)(statement, undefined, logicParseState); if (logicParseState.logicKind === 'declare') { logic.body = []; } addType2Namespace(app, logic, headNamespace, tailNamespace, logicParseState); } else { return (0, utils_1.throwError)(parseState, '不支持的命名空间:' + idNamesStr); } } else if (idNamesStr === 'app.backend.variables') { if (statement.type === 'VariableDeclaration') { if (!app.backend) { app.backend = new utils_1.naslTypes.Backend(); } const variable = (0, transform2Variable_1.transform2BackendVariable)(statement, { ...parseState }); variable && app.backend.variables.push(variable); } else return (0, utils_1.throwError)(parseState, '变量命名空间中不支持的节点类型', statement.type, statement); } else if (idNames[1] === 'frontendTypes' && idNames[3] === 'frontends') { let frontendType = app.frontendTypes.find((item) => item.name === idNames[2]); if (!frontendType) { frontendType = new utils_1.naslTypes.FrontendType({ name: idNames[2], kind: idNames[2], frameworkKind: "vue3", frameworkUI: "ElementPlus", }); app.frontendTypes.push(frontendType); } let frontend = frontendType.frontends.find((item) => item.name === idNames[4]); if (!frontend) { frontend = new utils_1.naslTypes.Frontend({ type: idNames[2], name: idNames[4], title: idNames[4].toUpperCase() + '端', path: '/', theme: new utils_1.naslTypes.Theme({ name: 'default', title: '默认主题样式', }) }); frontendType.frontends.push(frontend); } if (idNames[5] === 'variables') { if (statement.type === 'VariableDeclaration') { const variable = (0, transform2Variable_1.transform2FrontendVariable)(statement, { ...parseState }); variable && frontend.variables.push(variable); } else return (0, utils_1.throwError)(parseState, '前端变量命名空间中不支持的节点类型', statement.type, statement); } else if (idNames[5] === 'views') { if (statement.type === 'FunctionDeclaration' || statement.type === 'TSDeclareFunction') { let decorator; let prevStatement = namespaceStatements[index - 1]?.statement; if (prevStatement?.type === 'ExpressionStatement' && prevStatement.expression.type === 'CallExpression' && prevStatement.expression.callee.type === 'Identifier' && prevStatement.expression.callee.name === '$View') { decorator = prevStatement.expression; } const newOptions = { ...parseState, isInFrontend: true }; const view = statement.type === 'FunctionDeclaration' ? (0, parseNaturalTSXView_1.transform2View)(statement, decorator, newOptions) : (0, parseNaturalTSXView_1.transformTSDeclareFunction2View)(statement, decorator, newOptions); newOptions.nodeName = view.name; const parentPathSegments = []; // ['dashboard', 'abc'] for (let i = 6; i < idNames.length - 1; i += 2) { if (idNames[i + 1] === 'views') { parentPathSegments.push(idNames[i]); } else { return (0, utils_1.throwError)(newOptions, `页面没有按 .views. 间隔,不支持的命名空间:${idNamesStr}`); } } // 现在传过来时,需要先排序了,不然可能找不到父页面 function findViewByPath(views, pathSegments) { if (pathSegments.length === 0) return null; const targetName = pathSegments[0]; const foundView = views.find(v => v.name === targetName); if (!foundView) return null; if (pathSegments.length === 1) return foundView; return findViewByPath(foundView.children || [], pathSegments.slice(1)); } let parentViewList; if (parentPathSegments.length === 0) { parentViewList = frontend.views; } else { let parentView = viewCacheMap[parentPathSegments.join('/')]; if (!parentView) parentView = findViewByPath(frontend.views, parentPathSegments); if (!parentView) return (0, utils_1.throwError)(newOptions, `找不到父级页面:${idNames.slice(0, -1).join('.')}`); parentViewList = parentView.children; } if (view) { const existingView = parentViewList.find((item) => item.name === view.name); if (existingView) { if (utils_1.DEBUG) { return (0, utils_1.throwError)(newOptions, `${idNamesStr} 中存在同名页面:${view.name}`); } else { // 存在则保留原来页面的子页面,其他内容覆盖 delete view.children; Object.assign(existingView, view); } } else { parentViewList.push(view); viewCacheMap[[...parentPathSegments, view.name].join('/')] = view; } } } } else if (statement.type === 'ExpressionStatement' && statement.expression.type === 'TaggedTemplateExpression') { const expression = statement.expression; if (expression.tag.type === 'Identifier' && expression.tag.name === '$theme') { if (expression.quasi.expressions.length > 0) { return (0, utils_1.throwError)(parseState, '不支持在 $theme 中使用动态表达式'); } const css = expression.quasi.quasis[0].value.raw; const variableMap = (0, transformThemeAndStyle_1.transformTheme)(css, { ...parseState }); frontend.theme.scopeVariableMap = variableMap; } else if (expression.tag.type === 'Identifier' && expression.tag.name === '$extraStyle') { if (expression.quasi.expressions.length > 0) { return (0, utils_1.throwError)(parseState, '不支持在 $style 中使用动态表达式'); } const css = expression.quasi.quasis[0].value.raw; const cssRules = (0, transformThemeAndStyle_1.transformStyle)(css, { ...parseState }); frontend.theme.cssRules = cssRules; } } else { return (0, utils_1.throwError)(parseState, '不支持的命名空间:' + idNamesStr); } } else { return (0, utils_1.throwError)(parseState, '不支持的命名空间:' + idNamesStr); } }); return app; } exports.parseNaturalTS = parseNaturalTS; //# sourceMappingURL=parseNaturalTS.js.map