Skip to content

Commit

Permalink
Refactor to replace style-search for functionArgumentsSearch() (#…
Browse files Browse the repository at this point in the history
…5882)

Replacing `style-search` with `postcss-value-parser`.
  • Loading branch information
ybiquitous committed Feb 7, 2022
1 parent 154a832 commit 4b0f57d
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 33 deletions.
Expand Up @@ -63,7 +63,7 @@ const rule = (primary) => {

functionArgumentsSearch(
valueParser.stringify(valueNode).toLowerCase(),
'linear-gradient',
/^(-webkit-|-moz-|-o-)?linear-gradient$/i,

This comment has been minimized.

Copy link
@Mouvedia

Mouvedia Apr 21, 2022

Contributor

You forgot -ms-linear-gradient.

This comment has been minimized.

Copy link
@ybiquitous

ybiquitous Apr 22, 2022

Author Member

@Mouvedia Nice catch! I'll open a pull request soon. 👍🏼

This comment has been minimized.

Copy link
@ybiquitous

ybiquitous Apr 22, 2022

Author Member

Opened #6031

(expression, expressionIndex) => {
const firstArg = expression.split(',')[0].trim();

Expand Down
73 changes: 63 additions & 10 deletions lib/utils/__tests__/functionArgumentsSearch.test.js
Expand Up @@ -3,10 +3,17 @@
const functionArgumentsSearch = require('../functionArgumentsSearch');

it('passes function arguments to callback', () => {
expect.assertions(2);

functionArgumentsSearch('calc(1 + 3)', 'calc', (expression, expressionIndex) => {
expect(expression).toBe('1 + 3');
expect(expressionIndex).toBe(5);
});
});

it('passes function arguments to callback when a case with mutiple expressions', () => {
expect.assertions(2);

functionArgumentsSearch('4px 5px calc(1px + 3px)', 'calc', (expression, expressionIndex) => {
expect(expression).toBe('1px + 3px');
expect(expressionIndex).toBe(13);
Expand All @@ -25,33 +32,79 @@ it('works with nested functions', () => {
calcExpressionIndexes.push(expressionIndex);
},
);

expect(calcExpressions).toEqual(['calc(1px + 2px) + 3px', '1px + 2px']);
expect(calcExpressionIndexes).toEqual([13, 18]);
});

const colorFuncValue = 'color(red s(- 10%) s( - 10%))';
it('works with nested functions inside `color()`', () => {
expect.assertions(2);

functionArgumentsSearch(colorFuncValue, 'color', (expression, expressionIndex) => {
expect(expression).toBe('red s(- 10%) s( - 10%)');
expect(expressionIndex).toBe(6);
});
functionArgumentsSearch(
'color(red s(- 10%) s( - 10%))',
'color',
(expression, expressionIndex) => {
expect(expression).toBe('red s(- 10%) s( - 10%)');
expect(expressionIndex).toBe(6);
},
);
});

it('works with nested functions inside `s()`', () => {
const sExpressions = [];
const sExpressionIndexes = [];

functionArgumentsSearch(colorFuncValue, 's', (expression, expressionIndex) => {
functionArgumentsSearch('color(red s(- 10%) s( - 10%))', 's', (expression, expressionIndex) => {
sExpressions.push(expression);
sExpressionIndexes.push(expressionIndex);
});

expect(sExpressions).toEqual(['- 10%', ' - 10%']);
expect(sExpressionIndexes).toEqual([12, 21]);
});

it('works with interpolation', () => {
expect.assertions(2);

functionArgumentsSearch('url(http://$(host)/path)', 'url', (expression, expressionIndex) => {
expect(expression).toBe('http://$(host)/path');
expect(expressionIndex).toBe(4);
});
});

it('works with regex', () => {
expect.assertions(4);

functionArgumentsSearch(
'linear-gradient(#fff, #000)',
/^(-moz-)?linear-gradient$/,
(expression, expressionIndex) => {
expect(expression).toBe('#fff, #000');
expect(expressionIndex).toBe(16);
},
);

functionArgumentsSearch(
'-moz-linear-gradient(#fff, #000)',
/^(-moz-)?linear-gradient$/,
(expression, expressionIndex) => {
expect(expression).toBe('#fff, #000');
expect(expressionIndex).toBe(21);
},
);
});

it('ignores strings', () => {
expect.assertions(3);

functionArgumentsSearch('calc(1px)', 'calc', (expression, expressionIndex) => {
expect(expression).toBe('1px');
expect(expressionIndex).toBe(5);
});
functionArgumentsSearch('"calc(1px)"', 'calc', (expression, expressionIndex) => {
expect(expression).toBeNull();
expect(expressionIndex).toBeNull();
});

const callback = jest.fn();

functionArgumentsSearch('"calc(1px)"', 'calc', callback);

expect(callback).not.toHaveBeenCalled();
});
46 changes: 24 additions & 22 deletions lib/utils/functionArgumentsSearch.js
@@ -1,7 +1,9 @@
'use strict';

const balancedMatch = require('balanced-match');
const styleSearch = require('style-search');
const valueParser = require('postcss-value-parser');

const { assert, isString, isRegExp } = require('./validateTypes');

/**
* Search a CSS string for functions by name.
Expand All @@ -14,28 +16,28 @@ const styleSearch = require('style-search');
* as the arguments.
*
* @param {string} source
* @param {string} functionName
* @param {string | RegExp} functionName
* @param {(expression: string, expressionIndex: number) => void} callback
* @returns {void}
*/
module.exports = function functionArgumentsSearch(source, functionName, callback) {
styleSearch(
{
source,
target: functionName,
functionNames: 'check',
},
(match) => {
if (source[match.endIndex] !== '(') {
return;
}

const parensMatch = balancedMatch('(', ')', source.substr(match.startIndex));

if (!parensMatch) {
throw new Error(`No parens match: "${source}"`);
}

callback(parensMatch.body, match.endIndex + 1);
},
);
valueParser(source).walk((node) => {
if (node.type !== 'function') return;

const { value } = node;

if (isString(functionName) && value !== functionName) return;

if (isRegExp(functionName) && !functionName.test(node.value)) return;

const parensMatch = balancedMatch('(', ')', source.slice(node.sourceIndex));

assert(parensMatch);

const expression = parensMatch.body;
const parenLength = 1; // == '('
const expressionIndex = node.sourceIndex + value.length + parenLength;

callback(expression, expressionIndex);
});
};

0 comments on commit 4b0f57d

Please sign in to comment.