@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
JavaScript
;
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;