diff --git a/.eslintrc.js b/.eslintrc.js index 35ed473..98b6948 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -4,7 +4,7 @@ module.exports = { es2021: true, node: true, }, - extends: ["plugin:vue/essential", "plugin:prettier/recommended"], + extends: ["plugin:vue/vue3-recommended", "plugin:prettier/recommended"], parserOptions: { ecmaVersion: "latest", parser: "@typescript-eslint/parser", diff --git a/src/App.vue b/src/App.vue index 57b7bc9..64c88fc 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,5 +1,6 @@ diff --git a/src/examples/index.ts b/src/examples/index.ts new file mode 100644 index 0000000..4aadbd1 --- /dev/null +++ b/src/examples/index.ts @@ -0,0 +1,3 @@ +export const importExample = () => { + alert(1); +}; diff --git a/src/generator/index.ts b/src/generator/index.ts index 28e1b72..08ff8ec 100644 --- a/src/generator/index.ts +++ b/src/generator/index.ts @@ -1,14 +1,53 @@ /** - * 生成 SQL + * 生成 SQL 入口函数 * @param json */ -export function doGenerateSQL(json: InputJSON): string { - if (!json?.main) { - return ""; - } - const context = json; - const result = replaceParams(context.main, context); - return replaceSubSql(result, context); +export function doGenerateSQL(json: InputJSON) { + // 缺失入口 + if (!json?.main) { + return null; + } + const sql = json.main.sql ?? json.main; + if (!sql) { + return null; + } + const initTreeNode = { + title: "main", + sql, + children: [], + }; + const rootInvokeTreeNode = { ...initTreeNode }; + const context = json; + const resultSQL = generateSQL( + context.main, + context, + context.main?.params, + rootInvokeTreeNode + ); + return { + resultSQL, + invokeTree: rootInvokeTreeNode, + }; +} + +/** + * 递归生成 SQL + * @param currentNode + * @param context + * @param params + * @param invokeTreeNode + */ +function generateSQL( + currentNode: InputJSONValue, + context: InputJSON, + params?: Record, + invokeTreeNode?: InvokeTreeNode +): string { + if (!currentNode) { + return ""; + } + const result = replaceParams(currentNode, context, params, invokeTreeNode); + return replaceSubSql(result, context, invokeTreeNode); } /** @@ -16,78 +55,112 @@ export function doGenerateSQL(json: InputJSON): string { * @param currentNode * @param context * @param params 动态参数 + * @param invokeTreeNode */ -function replaceParams(currentNode: InputJSONValue, context: InputJSON, params?: Record): string { - if (currentNode == null) { - return ""; - } - const sql = currentNode.sql ?? currentNode; - if (!sql) { - return ""; - } - // 动态、静态参数结合,且优先用静态参数 - params = {...(params ?? {}), ...currentNode.params}; - // 无需替换 - if (!params || Object.keys(params).length < 1) { - return sql; - } - let result = sql; - for (const paramsKey in params) { - const replacedKey = `#{${paramsKey}}`; - // 递归解析 - const replacement = replaceSubSql(params[paramsKey], context); - // find and replace - result = result.replaceAll(replacedKey, replacement); - } - return result; +function replaceParams( + currentNode: InputJSONValue, + context: InputJSON, + params?: Record, + invokeTreeNode?: InvokeTreeNode +): string { + if (currentNode == null) { + return ""; + } + const sql = currentNode.sql ?? currentNode; + if (!sql) { + return ""; + } + // 动态、静态参数结合,且优先用静态参数 + params = { ...(params ?? {}), ...currentNode.params }; + // 无需替换 + if (!params || Object.keys(params).length < 1) { + return sql; + } + let result = sql; + for (const paramsKey in params) { + const replacedKey = `#{${paramsKey}}`; + // 递归解析 + // const replacement = replaceSubSql( + // params[paramsKey], + // context, + // invokeTreeNode + // ); + const replacement = params[paramsKey]; + // find and replace + result = result.replaceAll(replacedKey, replacement); + } + return result; } /** * 替换子 SQL(@xxx) * @param sql * @param context + * @param invokeTreeNode */ -function replaceSubSql(sql: string, context: InputJSON): string { - if (!sql) { - return ""; +function replaceSubSql( + sql: string, + context: InputJSON, + invokeTreeNode?: InvokeTreeNode +): string { + if (!sql) { + return ""; + } + let result = sql; + result = String(result); + let regExpMatchArray = matchSubQuery(result); + // 依次替换 + while (regExpMatchArray && regExpMatchArray.length > 2) { + // 找到结果 + const subKey = regExpMatchArray[1]; + // 可用来替换的节点 + const replacementNode = context[subKey]; + // 没有可替换的节点 + if (!replacementNode) { + const errorMsg = `${subKey} 不存在`; + alert(errorMsg); + throw new Error(errorMsg); + } + // 获取要传递的动态参数 + // e.g. "a = b, c = d" + let paramsStr = regExpMatchArray[2]; + if (paramsStr) { + paramsStr = paramsStr.trim(); } - let result = sql; - result = String(result); - let regExpMatchArray = matchSubQuery(result); - // 依次替换 - while (regExpMatchArray && regExpMatchArray.length > 2) { - // 找到结果 - const subKey = regExpMatchArray[1]; - // 可用来替换的节点 - const replacementNode = context[subKey]; - // 没有可替换的节点 - if (!replacementNode) { - throw new Error(`${subKey} 不存在`); - } - // 获取要传递的动态参数 - // e.g. "a = b, c = d" - let paramsStr = regExpMatchArray[2]; - if (paramsStr) { - paramsStr = paramsStr.trim(); - } - // e.g. ["a = b", "c = d"] - const singleParamsStrArray = paramsStr.split('|||'); - // string => object - const params: Record = {}; - for (const singleParamsStr of singleParamsStrArray) { - // 必须分成 2 段 - const keyValueArray = singleParamsStr.split('=', 2); - if (keyValueArray.length < 2) { - continue; - } - const key = keyValueArray[0].trim(); - params[key] = keyValueArray[1].trim(); - } - const replacement = replaceParams(replacementNode, context, params); - result = result.replaceAll(regExpMatchArray[0], replacement); - regExpMatchArray = matchSubQuery(result); + // e.g. ["a = b", "c = d"] + const singleParamsStrArray = paramsStr.split("|||"); + // string => object + const params: Record = {}; + for (const singleParamsStr of singleParamsStrArray) { + // 必须分成 2 段 + const keyValueArray = singleParamsStr.split("=", 2); + if (keyValueArray.length < 2) { + continue; + } + const key = keyValueArray[0].trim(); + params[key] = keyValueArray[1].trim(); } - return result; + let childInvokeTreeNode; + if (invokeTreeNode) { + childInvokeTreeNode = { + title: subKey, + sql, + params, + children: [], + }; + invokeTreeNode.children?.push(childInvokeTreeNode); + } + // 递归解析被替换节点 + const replacement = generateSQL( + replacementNode, + context, + params, + childInvokeTreeNode + ); + result = result.replace(regExpMatchArray[0], replacement); + regExpMatchArray = matchSubQuery(result); + } + return result; } /** @@ -95,41 +168,41 @@ function replaceSubSql(sql: string, context: InputJSON): string { * @param str */ function matchSubQuery(str: string) { - if (!str) { - return null; - } - const regExp = /@([\u4e00-\u9fa5_a-zA-Z0-9]+)\((.*?)\)/; - let regExpMatchArray = str.match(regExp); - if (!regExpMatchArray || regExpMatchArray.index === undefined) { - return null; + if (!str) { + return null; + } + const regExp = /@([\u4e00-\u9fa5_a-zA-Z0-9]+)\((.*?)\)/; + let regExpMatchArray = str.match(regExp); + if (!regExpMatchArray || regExpMatchArray.index === undefined) { + return null; + } + // @ 开始位置 + let startPos = regExpMatchArray.index; + // 左括号右侧 + let leftParenthesisPos = startPos + regExpMatchArray[1].length + 2; + // 遍历游标 + let currPos = leftParenthesisPos; + // 默认匹配结束位置,需要对此结果进行修正 + let endPos = startPos + regExpMatchArray[0].length; + // 剩余待匹配左括号数量 + let leftCount = 1; + while (currPos < str.length) { + const currentChar = str.charAt(currPos); + if (currentChar === "(") { + leftCount++; + } else if (currentChar === ")") { + leftCount--; } - // @ 开始位置 - let startPos = regExpMatchArray.index; - // 左括号右侧 - let leftParenthesisPos = startPos + regExpMatchArray[1].length + 2; - // 遍历游标 - let currPos = leftParenthesisPos; - // 默认匹配结束位置,需要对此结果进行修正 - let endPos = startPos + regExpMatchArray[0].length; - // 剩余待匹配左括号数量 - let leftCount = 1; - while (currPos < str.length) { - const currentChar = str.charAt(currPos); - if (currentChar === '(') { - leftCount++; - } else if (currentChar === ')') { - leftCount--; - } - // 匹配结束 - if (leftCount == 0) { - endPos = currPos + 1; - break; - } - currPos++; + // 匹配结束 + if (leftCount == 0) { + endPos = currPos + 1; + break; } - return [ - str.slice(startPos, endPos), - regExpMatchArray[1], - str.slice(leftParenthesisPos, endPos - 1) - ] -} \ No newline at end of file + currPos++; + } + return [ + str.slice(startPos, endPos), + regExpMatchArray[1], + str.slice(leftParenthesisPos, endPos - 1), + ]; +} diff --git a/src/generator/type.d.ts b/src/generator/type.d.ts index 1cbd594..6fd20bb 100644 --- a/src/generator/type.d.ts +++ b/src/generator/type.d.ts @@ -12,8 +12,9 @@ interface InputJSONValue { * 调用树节点 */ interface InvokeTreeNode { - label: string; + title: string; sql: string; + key?: string; params?: Record; children?: InvokeTreeNode[]; }