Skip to content

Commit

Permalink
Fix: replace multiple variables after each other (closes #137) (#138)
Browse files Browse the repository at this point in the history
  • Loading branch information
JPeer264 authored Jul 14, 2021
1 parent 47f88e2 commit aa4fec1
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 68 deletions.
15 changes: 15 additions & 0 deletions __tests__/helpers/arrayToRegex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import arrayToRegex from '../../lib/helpers/arrayToRegex';

describe('arrayToRegex', () => {
it('should return null on empty array', () => {
expect(arrayToRegex([])).toBeNull();
});

it('should sort by length', () => {
expect(arrayToRegex(['bb', 'ccc', 'a'])).toStrictEqual(/(ccc)|(bb)|(a)/g);
});

it('should add a specific modifier', () => {
expect(arrayToRegex(['bb', 'a', 'ccc'], (a) => `--${a}`)).toStrictEqual(/(--ccc)|(--bb)|(--a)/g);
});
});
63 changes: 31 additions & 32 deletions __tests__/replace.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -588,38 +588,6 @@ it('replace css variables in calc and multiple variables | rename-css-selectors#
);
});

it('replace css variables with deep nested and multiline | rename-css-selectors#43', () => {
replaceCssMacro(
`
:root {
--top: 2px;
--right: 2px;
--left: 2px;
}
.my-selector {
margin: var(--top, var(--right))
var(--right, var(--top, 3px))
5px
var(--left, var(--top, var(--not-renamed, 5px)))
}
`,
`
:root {
--a: 2px;
--b: 2px;
--c: 2px;
}
.a {
margin: var(--a, var(--b))
var(--b, var(--a, 3px))
5px
var(--c, var(--a, var(--not-renamed, 5px)))
}
`,
);
});

it('should replace excluded special characters | rename-css-selectors#77', () => {
rcs.selectorsLibrary.setExclude('somediv:test-me');
Expand All @@ -645,3 +613,34 @@ it('should classes with and ignore them | rcs-core#133', () => {
'.a.bottom-\\[99999px\\][class="test"] {bottom: 99999px;}',
);
});

it('replace multiple variables after each other | #137', () => {
replaceCssMacro(
`
*, ::before, ::after {
--tw-shadow: 0 0 transparent;
--tw-ring-offset-shadow: 0 0 transparent;
--tw-ring-shadow: 0 0 transparent;
}
.shadow-small {
--tw-shadow: 0 2px 4px 0 rgb(151, 145, 151, 0.1);
box-shadow: 0 0 transparent, 0 0 transparent, var(--tw-shadow);
box-shadow: var(--tw-ring-offset-shadow, 0 0 transparent), var(--tw-ring-shadow, 0 0 transparent), var(--tw-shadow);
}
`,
`
*, ::before, ::after {
--a: 0 0 transparent;
--b: 0 0 transparent;
--c: 0 0 transparent;
}
.a {
--a: 0 2px 4px 0 rgb(151, 145, 151, 0.1);
box-shadow: 0 0 transparent, 0 0 transparent, var(--a);
box-shadow: var(--b, 0 0 transparent), var(--c, 0 0 transparent), var(--a);
}
`,
);
});
8 changes: 4 additions & 4 deletions __tests__/selectorsLibrary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ it('getall | should return a regex of compressed with classes', () => {
addSelectorType: true,
});

expect(regex.source).toBe('(\\#a)|(\\.a|\\.b)');
expect(regex.source).toBe('(\\.a|\\.b)|(\\#a)');
});

it('getall | should return an array with selectors', () => {
Expand Down Expand Up @@ -293,7 +293,7 @@ it('getall | should return a regex of non compressed with classes', () => {
addSelectorType: true,
});

expect(regex.source).toBe('(\\#id)|(\\.bottom-\\[9px]|\\.test)');
expect(regex.source).toBe('(\\.bottom-\\[9px]|\\.test)|(\\#id)');
});

it('getall | should return a regex of non compressed selecotrs', () => {
Expand All @@ -303,15 +303,15 @@ it('getall | should return a regex of non compressed selecotrs', () => {
getRenamedValues: true,
});

expect(regex.source).toBe('((\\s|\\#)(a)[\\s)])|((\\s|\\.)(a|b)[\\s)])');
expect(regex.source).toBe('((\\s|\\.)(a|b)[\\s)])|((\\s|\\#)(a)[\\s)])');
});

it('getall | should return a regex of compressed selectors', () => {
setSelectors();

const regex = rcs.selectorsLibrary.getAllRegex();

expect(regex.source).toBe('((\\s|\\#)(id)[\\s)])|((\\s|\\.)(bottom-\\[9px]|test)[\\s)])');
expect(regex.source).toBe('((\\s|\\.)(bottom-\\[9px]|test)[\\s)])|((\\s|\\#)(id)[\\s)])');
});

it('getall | should get all setted classes', () => {
Expand Down
20 changes: 20 additions & 0 deletions lib/helpers/arrayToRegex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
function arrayToRegex(
values: string[],
modifier: (value: string) => string = (value) => value,
): RegExp | null {
if (!values.length) {
return null;
}

return (
new RegExp(`(${values
// sort by size
.sort((a, b) => b.length - a.length)
// add -- to ensure to only capture attributes
.map(modifier)
.join(')|(')
})`, 'g')
);
}

export default arrayToRegex;
39 changes: 8 additions & 31 deletions lib/replace/css.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,11 @@
import { parse } from 'postcss';

import cssVariablesLibrary from '../cssVariablesLibrary';
import arrayToRegex from '../helpers/arrayToRegex';
import keyframesLibrary from '../keyframesLibrary';
import selectorsLibrary from '../selectorsLibrary';
import replaceRegex from './regex';

const extractCssVariables = (value: string): string[] => {
const regexMatches = value.match(new RegExp(replaceRegex.cssVariables));

let matches: string[] = [];

if (regexMatches) {
regexMatches.forEach((matchWithVariables) => {
const cssVariableMatch = new RegExp(replaceRegex.cssVariables).exec(matchWithVariables);

if (!cssVariableMatch) {
return;
}

matches = [...matches, cssVariableMatch[1]];

if (cssVariableMatch[2]) {
const innerMatches = extractCssVariables(cssVariableMatch[2]);

matches = [...matches, ...innerMatches];
}
});
}

return [...(new Set(matches))];
};

// calls the selectorLibrary.getAttributeSelector internally
// String.replace will call this function and
// get call selectorLibrary.getAttributeSelector directly
Expand Down Expand Up @@ -180,12 +155,14 @@ const replaceCss = (css: string | Buffer, opts: ReplaceCssOptions = {}): string
* replace css variables var() *
* *************************** */
if (node.value.match(replaceRegex.cssVariables)) {
const matches = extractCssVariables(node.value);
const regex = arrayToRegex(Object.keys(cssVariablesLibrary.values), (v) => `--${v}`);

// eslint-disable-next-line no-param-reassign
node.value = node.value.replace(new RegExp(matches.join('|'), 'g'), (match: string) => (
cssVariablesLibrary.get(match, { source })
));
if (regex) {
// eslint-disable-next-line no-param-reassign
node.value = node.value.replace(regex, (match: string) => (
`--${cssVariablesLibrary.get(match.replace(/^--/, ''), { source })}`
));
}
}

/* ******************************************** *
Expand Down
5 changes: 4 additions & 1 deletion lib/selectorsLibrary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { AttributeLibrary } from './attributeLibrary';
import idSelectorLibrary, { IdSelectorLibrary } from './idSelectorLibrary';
import classSelectorLibrary, { ClassSelectorLibrary } from './classSelectorLibrary';
import { BaseLibrary, BaseLibraryOptions } from './baseLibrary';
import arrayToRegex from './helpers/arrayToRegex';

// Simple aggregate class to avoid duplicating code dealing with any CSS selector.
export class SelectorsLibrary extends BaseLibrary {
Expand Down Expand Up @@ -127,7 +128,9 @@ export class SelectorsLibrary extends BaseLibrary {

const ret = this.callOnBoth('getAll', options);

return new RegExp(`(${ret.filter(Boolean).map((x) => x.source).join(')|(')})`, 'g');
// null assertion to keep the same functionality
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return arrayToRegex(ret.filter(Boolean).map((x) => x.source))!;
}

get(value: string, opts: BaseLibraryOptions = {}): string {
Expand Down

0 comments on commit aa4fec1

Please sign in to comment.