UNPKG

@codeque/core

Version:

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

293 lines (238 loc) 8.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.cssTree = void 0; var _cssTree = require("css-tree"); var _utils = require("../../utils"); var _common = require("./common"); var _traverseAndMatch = require("../../searchStages/traverseAndMatch"); var _beforeWildcardsComparators = require("./beforeWildcardsComparators"); var _afterWildcardsComparators = require("./afterWildcardsComparators"); const supportedExtensions = ['css']; const getProgramNodeFromRootNode = rootNode => rootNode; // root node is program node const getProgramBodyFromRootNode = fileNode => { return fileNode.children; }; const unwrapExpressionStatement = node => { return node; }; const createBlockStatementNode = (children, position) => ({ type: 'Block', children, ...position }); const isNode = maybeNode => { return typeof maybeNode?.type === 'string'; }; const astPropsToSkip = ['loc']; const parseCode = code => { const sharedOptions = { parseAtrulePrelude: true, parseRulePrelude: true, parseValue: true, positions: true }; if (code.includes('{')) { return (0, _cssTree.toPlainObject)((0, _cssTree.parse)(code, { ...sharedOptions, context: 'stylesheet' })); } else { return (0, _cssTree.toPlainObject)((0, _cssTree.parse)(code, { ...sharedOptions, context: 'declarationList' })); } }; const nodeValuesSanitizers = { ['Raw']: { value: _utils.normalizeText } }; const getSanitizedNodeValue = (nodeType, valueKey, value) => { const valueSanitizer = nodeValuesSanitizers?.[nodeType]?.[valueKey]; if (valueSanitizer) { return valueSanitizer(value); } return value; }; const shouldCompareNode = node => { if (node.type === 'WhiteSpace') { return false; } if (node.type === 'Raw') { const value = getSanitizedNodeValue('Raw', 'value', node.value); const shouldCompare = value.length > 0; return shouldCompare; } return true; }; const getNodeType = node => node.type; const isIdentifierNode = node => _common.identifierNodeTypes.includes(getNodeType(node)); const stringLikeNodeTypes = ['TypeSelector', 'Raw', 'ClassSelector', 'Identifier', 'IdSelector', 'Url']; const stringLikeLiteralUtils = { // Raw is only pure string node isStringLikeLiteralNode: node => stringLikeNodeTypes.includes(node.type), getStringLikeLiteralValue: node => { return node?.value || node?.name; } }; const pureNumericNodes = ['Percentage', 'Number', 'Hash']; const numericLiteralUtils = { isNumericLiteralNode: node => pureNumericNodes.includes(node.type), getNumericLiteralValue: node => node.value }; const programNodeAndBlockNodeUtils = { isProgramNode: node => node.type === 'StyleSheet', isBlockNode: node => node.type === 'Block', programNodeBodyKey: 'children', blockNodeBodyKey: 'children' }; const getNodePosition = node => { const location = node.loc; return { start: location?.start?.offset ?? 0, end: location?.end?.offset ?? 0, loc: { start: { line: location.start.line, column: location.start.column - 1 // We need 0-based, parser return 1-based }, end: { line: location.end.line, column: location.end.column - 1 // We need 0-based, parser return 1-based } } }; }; const getParseErrorLocation = e => ({ line: e.loc?.line ?? 0, column: e.loc?.column ?? 0 }); const alternativeNodeTypes = { Identifier: _common.identifierNodeTypes }; /** * To support wildcards in caa we have to * - encode wildcard, do it in query text before parsing $$ => a_a_x * - decode wildcard, traverse parsed query and: a_a_x => $$ * - Same for numeric wildcard 0x0 -> 00000000 // 0{8} * `$$` is invalid tag name start in all html parsers */ const encodedStringWildcardSequence = 'a_a_a'; const encodedNodesTreeWildcardSequence = 'z_z_z'; const encodedNumericWildcardSequence = '00000000'; const preprocessQueryCode = code => { const queryCode = code.replace(/(\$\$\$)/g, () => encodedNodesTreeWildcardSequence).replace(/(\$\$)/g, () => encodedStringWildcardSequence).replace(/0x0/g, encodedNumericWildcardSequence); return queryCode; }; const replaceEncodedWildcards = value => value.replace(/a_a_a/g, () => '$$').replace(/z_z_z/g, () => '$$$').replace(/0{8}/g, '0x0'); const stringNodeTypes = { withName: ['Identifier', 'IdSelector', 'MediaFeature', 'ClassSelector', 'PseudoClassSelector', 'PseudoElementSelector', 'TypeSelector', 'Function', 'Combinator'], withValue: ['String', 'Url'], withProperty: ['Declaration'] }; const postprocessQueryNodeWithName = node => { const name = node.name; if (name.includes(encodedStringWildcardSequence) || name.includes(encodedNodesTreeWildcardSequence)) { node.name = replaceEncodedWildcards(name); } }; const postprocessQueryNodeWithValue = node => { const value = node.value; if (value.includes(encodedStringWildcardSequence) || value.includes(encodedNodesTreeWildcardSequence) || value.includes(encodedNumericWildcardSequence)) { node.value = replaceEncodedWildcards(value); } }; const postprocessQueryNodeWithProperty = node => { const property = node.property; if (property.includes(encodedStringWildcardSequence) || property.includes(encodedNodesTreeWildcardSequence)) { node.property = replaceEncodedWildcards(property); } }; const createVisitorsForNodeTypes = (types, visitorFn) => types.reduce((visitorsMap, nodeType) => ({ ...visitorsMap, [nodeType]: visitorFn }), {}); const postprocessVisitors = { ...createVisitorsForNodeTypes(stringNodeTypes.withName, postprocessQueryNodeWithName), ...createVisitorsForNodeTypes(stringNodeTypes.withProperty, postprocessQueryNodeWithProperty), ...createVisitorsForNodeTypes([...stringNodeTypes.withValue, ...pureNumericNodes], postprocessQueryNodeWithValue), Dimension: node => { const unit = node.unit; const value = node.value; if (unit.includes(encodedStringWildcardSequence)) { node.unit = replaceEncodedWildcards(unit); } if (value === encodedNumericWildcardSequence) { node.value = replaceEncodedWildcards(value); } } }; const postprocessQueryNode = queryNode => { (0, _traverseAndMatch.traverseAst)(queryNode, isNode, getNodeType, postprocessVisitors); return queryNode; }; const compareNodesBeforeWildcardsComparison = (...nodeComparatorParams) => { return (0, _utils.runNodesComparators)(_beforeWildcardsComparators.beforeWildcardsComparators, nodeComparatorParams); }; const compareNodesAfterWildcardsComparison = (...nodeComparatorParams) => { return (0, _utils.runNodesComparators)(_afterWildcardsComparators.afterWildcardsComparators, nodeComparatorParams); }; const getUniqueTokensFromStringOrIdentifierNode = ({ queryNode, caseInsensitive, parserSettings }) => { const MIN_TOKEN_LEN = 2; const { anyStringWildcardRegExp } = parserSettings.wildcardUtils; const tokens = []; const valuesToProcess = []; if (stringNodeTypes.withName.includes(queryNode.type)) { valuesToProcess.push(queryNode.name); } if (stringNodeTypes.withProperty.includes(queryNode.type)) { valuesToProcess.push(queryNode.property); } if (stringNodeTypes.withValue.includes(queryNode.type)) { valuesToProcess.push(queryNode.value); } valuesToProcess.map(val => parserSettings.wildcardUtils.removeWildcardAliasesFromStringLiteral(val)).map(val => (0, _utils.decomposeString)(val, anyStringWildcardRegExp)).flat(1).forEach(part => { if (part.length >= MIN_TOKEN_LEN) { tokens.push(caseInsensitive ? part.toLocaleLowerCase() : part); } }); return tokens; }; const cssTree = { supportedExtensions, parseCode, isNode, isIdentifierNode, astPropsToSkip, getProgramBodyFromRootNode, getProgramNodeFromRootNode, getIdentifierNodeName: _common.getIdentifierNodeName, getNodeType, unwrapExpressionStatement, createBlockStatementNode, getSanitizedNodeValue, identifierNodeTypes: _common.identifierNodeTypes, setIdentifierNodeName: _common.setIdentifierNodeName, shouldCompareNode, wildcardUtils: _common.wildcardUtils, compareNodesBeforeWildcardsComparison, compareNodesAfterWildcardsComparison, identifierTypeAnnotationFieldName: 'typeAnnotation', stringLikeLiteralUtils, numericLiteralUtils, programNodeAndBlockNodeUtils, getNodePosition, getParseErrorLocation, alternativeNodeTypes, postprocessQueryNode, preprocessQueryCode, getUniqueTokensFromStringOrIdentifierNode }; exports.cssTree = cssTree; var _default = cssTree; exports.default = _default;