@lcap/nasl
Version:
NetEase Application Specific Language
484 lines • 25.1 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;
};
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