UNPKG

@codeque/core

Version:

Multiline code search for every language. Structural code search for JavaScript, TypeScript, HTML and CSS

265 lines (231 loc) 7.62 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.parseQueries = exports.getUniqueTokens = exports.getHints = exports.extractQueryNode = void 0; var _utils = require("./utils"); var _astUtils = require("./astUtils"); const MIN_TOKEN_LEN = 2; const defaultGetUniqueTokensFromStringOrIdentifierNode = ({ queryNode, caseInsensitive, parserSettings }) => { const { stringLikeLiteralUtils, getIdentifierNodeName } = parserSettings; const { anyStringWildcardRegExp } = parserSettings.wildcardUtils; const tokens = []; if (parserSettings.isIdentifierNode(queryNode)) { const trimmedWildcards = parserSettings.wildcardUtils.removeWildcardAliasesFromIdentifierName(getIdentifierNodeName(queryNode)).split(parserSettings.wildcardUtils.identifierWildcard); trimmedWildcards.forEach(part => { if (part.length >= MIN_TOKEN_LEN) { tokens.push(caseInsensitive ? part.toLocaleLowerCase() : part); } }); } if (stringLikeLiteralUtils.isStringLikeLiteralNode(queryNode)) { const stringContent = parserSettings.wildcardUtils.removeWildcardAliasesFromStringLiteral(stringLikeLiteralUtils.getStringLikeLiteralValue(queryNode)); const trimmedWildcards = (0, _utils.decomposeString)(stringContent, anyStringWildcardRegExp); trimmedWildcards.forEach(part => { if (part.length >= MIN_TOKEN_LEN) { tokens.push(caseInsensitive ? part.toLocaleLowerCase() : part); } }); } return tokens; }; const getUniqueTokens = (queryNode, caseInsensitive, parserSettings, tokens = new Set()) => { const { numericLiteralUtils, getUniqueTokensFromStringOrIdentifierNode } = parserSettings; const getUniqueTokensFn = getUniqueTokensFromStringOrIdentifierNode ?? defaultGetUniqueTokensFromStringOrIdentifierNode; const tokensFromStringsOrIdNode = getUniqueTokensFn({ queryNode, caseInsensitive, parserSettings }); tokensFromStringsOrIdNode.forEach(tokens.add, tokens); if (numericLiteralUtils.isNumericLiteralNode(queryNode)) { const raw = numericLiteralUtils.getNumericLiteralValue(queryNode); if (raw !== parserSettings.wildcardUtils.numericWildcard) { tokens.add(caseInsensitive ? raw.toLocaleLowerCase() : raw); } } const nodeKeys = (0, _astUtils.getKeysToCompare)(queryNode, parserSettings.astPropsToSkip, parserSettings.getNodeType).filter(key => parserSettings.isNode(queryNode[key]) || (0, _astUtils.isNodeArray)(queryNode[key], parserSettings.isNode)); nodeKeys.forEach(key => { const nodeVal = queryNode[key]; if ((0, _astUtils.isNodeArray)(nodeVal, parserSettings.isNode)) { const _nodeVal = nodeVal; _nodeVal.forEach(node => getUniqueTokens(node, caseInsensitive, parserSettings, tokens)); } else { getUniqueTokens(nodeVal, caseInsensitive, parserSettings, tokens); } }); return tokens; }; exports.getUniqueTokens = getUniqueTokens; const extractQueryNode = (topLevelQueryNode, parserSettings) => { const queryBody = parserSettings.getProgramBodyFromRootNode(topLevelQueryNode); if (queryBody.length === 0) { throw new Error('Query is empty or code was not parsed correctly'); } if (queryBody.length === 1) { return { queryNode: parserSettings.unwrapExpressionStatement(queryBody[0]), isMultistatement: false }; } const position = parserSettings.getNodePosition(topLevelQueryNode); return { queryNode: parserSettings.createBlockStatementNode(queryBody, position), isMultistatement: true }; }; exports.extractQueryNode = extractQueryNode; const getHints = (queryCode, error) => { const hints = []; if (queryCode.startsWith('{')) { const info = 'To look for object, add expression brackets'; const code = '({ key:val })'; hints.push({ text: `${info} ${code}`, tokens: [{ type: 'text', content: info }, { type: 'code', content: code }] }); } if (error && (queryCode.startsWith("'") || queryCode.startsWith('"')) && (error.text.includes('Unterminated string constant') || error.text.includes('Empty query'))) { const info = 'To look for string, add expression brackets'; const code = "('some string')"; hints.push({ text: `${info} ${code}`, tokens: [{ type: 'text', content: info }, { type: 'code', content: code }] }); } return hints; }; exports.getHints = getHints; const parseQueries = (queryCodes, caseInsensitive, parserSettings) => { const inputQueryNodes = queryCodes.map(queryText => { let originalError = null; if (parserSettings.wildcardUtils.disallowedWildcardRegExp.test(queryText)) { const lines = queryText.split('\n'); let lineIdx = null; let colNum = null; lines.forEach((line, idx) => { const col = line.indexOf(parserSettings.wildcardUtils.disallowedWildcardSequence); if (colNum === null && col > -1) { lineIdx = idx; colNum = col + 1; } }); return { queryNode: {}, isMultistatement: false, error: { text: 'More than three wildcard chars are not allowed', ...(colNum !== null && lineIdx !== null ? { location: { line: lineIdx + 1, column: colNum } } : {}) } }; } if (queryText.trim().length === 0) { return { queryNode: null, error: { text: 'Empty query!' }, isMultistatement: false }; } const preprocessedQueryCode = parserSettings.preprocessQueryCode?.(queryText) ?? queryText; try { const parsedAsIs = parserSettings.parseCode(preprocessedQueryCode.trim()); const { queryNode, isMultistatement } = extractQueryNode(parsedAsIs, parserSettings); return { queryNode, isMultistatement, error: null }; } catch (e) { const error = e; originalError = { text: error.message, location: parserSettings.getParseErrorLocation(error), code: error.code, reasonCode: error.reasonCode }; } // TODO move to parse code, specific to JS try { const parsedAsExp = parserSettings.parseCode(`(${preprocessedQueryCode})`); const { queryNode } = extractQueryNode(parsedAsExp, parserSettings); return { queryNode, isMultistatement: false, // single expression cannot be multistatement error: null }; } catch (e) { return { queryNode: {}, isMultistatement: false, error: originalError }; } }).map(({ error, queryNode, isMultistatement }) => ({ queryNode: (queryNode && parserSettings.postprocessQueryNode?.(queryNode)) ?? queryNode, error: !queryNode ? { text: 'Empty query!' } : error, isMultistatement })); const queries = inputQueryNodes.map(({ queryNode, error, isMultistatement }, i) => { const uniqueTokens = queryNode ? [...getUniqueTokens(queryNode, caseInsensitive, parserSettings)].filter(token => typeof token !== 'string' || token.length > 0) : []; const queryCode = queryCodes[i]; const hints = getHints(queryCode, error); return { hints, queryNode, queryCode, uniqueTokens, error, isMultistatement }; }); return [queries, queries.filter(({ error }) => error !== null).length === 0]; }; exports.parseQueries = parseQueries;