diff --git a/CHANGELOG.md b/CHANGELOG.md index 92d2a6e5..faafca7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## [3.3.4 / 1.7.3] - 2021-05-23 + +- Added a new option to support declarations aliases + ## [3.3.3 / 1.7.2] - 2021-05-23 - Added a new option to ignore rules already prefixed diff --git a/README.md b/README.md index b98d4058..a39a7629 100644 --- a/README.md +++ b/README.md @@ -329,7 +329,8 @@ All the options are optional, and a default value will be used if any of them is | useCalc | `boolean` | `false` | Flips `background-position`, `background-position-x` and `transform-origin` properties if they are expressed in length units using [calc](https://developer.mozilla.org/en-US/docs/Web/CSS/calc) | | stringMap | `PluginStringMap[]` | Check below | An array of strings maps that will be used to make the replacements of the URLs and rules selectors names | | autoRename | `Autorename (string)` | `Autorename.disabled` | Flip or not the selectors names of the rules without directional properties using the `stringMap` | -| greedy | `boolean ` | `false` | When `autoRename` is enabled and greedy is `true`, the strings replacements will not take into account word boundaries | +| greedy | `boolean` | `false` | When `autoRename` is enabled and greedy is `true`, the strings replacements will not take into account word boundaries | +| aliases | `Record` | `{}` | A strings map to treat some declarations as others | --- @@ -1150,6 +1151,71 @@ const options = { --- +#### aliases + +
Expand +

+ +This property consists of a string map to treat some declarations as others, very useful to flip the values of [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) + +##### input + +```css +:root { + --my-padding: 1rem 1rem 1.5rem 1.5rem; +} + +.test { + padding: var(--my-padding); +} +``` + +##### No aliases string map (default) + +##### output + +```css +:root { + --my-padding: 1rem 1rem 1.5rem 1.5rem; +} + +.test { + padding: var(--my-padding); +} +``` + +##### Set an aliases string map + +```javascript +const options = { + aliases: { + '--my-padding': 'padding' + } +}; +``` + +##### output + +```css +[dir="ltr"]:root { + --my-padding: 1rem 1rem 1.5rem 1.5rem; +} + +[dir="rtl"]:root { + --my-padding: 1rem 1.5rem 1.5rem 1rem; +} + +.test { + padding: var(--my-padding); +} +``` + +

+ +
+ +--- + Control Directives --- diff --git a/package.json b/package.json index 94a0871d..d06272e7 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "url": "git+https://github.com/elchininet/postcss-rtlcss" }, "dependencies": { - "rtlcss": "^3.1.2" + "rtlcss": "^3.2.0" }, "devDependencies": { "@types/eslint": "^7.2.10", diff --git a/src/@types/index.ts b/src/@types/index.ts index da26ae56..b48b6029 100644 --- a/src/@types/index.ts +++ b/src/@types/index.ts @@ -55,6 +55,7 @@ export interface PluginOptions { stringMap?: PluginStringMap[]; autoRename?: AutorenameValues; greedy?: boolean; + aliases?: Record; } export interface PluginOptionsNormalized extends Omit, 'stringMap'> { diff --git a/src/data/store.ts b/src/data/store.ts index 6324c322..8ecb52a6 100644 --- a/src/data/store.ts +++ b/src/data/store.ts @@ -98,6 +98,12 @@ const isNotAcceptedStringMap = (stringMap: PluginStringMap[]): boolean => { ); }; +const isObjectWithStringKeys = (obj: Record): boolean => + !Object.entries(obj).some( + (entry: [string, unknown]): boolean => + typeof entry[1] !== 'string' + ); + const spreadArrayOfStrings = (arr: string[], item: strings): string[] => { return typeof item === 'string' ? [...arr, item] @@ -133,7 +139,8 @@ const defaultOptions = (): PluginOptionsNormalized => ({ useCalc: false, stringMap: getRTLCSSStringMap(defaultStringMap), autoRename: Autorename.disabled, - greedy: false + greedy: false, + aliases: {} }); const store: Store = { @@ -196,6 +203,9 @@ export const normalizeOptions = (options: PluginOptions): PluginOptionsNormalize } }); } + if (options.aliases && isObjectWithStringKeys(options.aliases)) { + returnOptions.aliases = options.aliases; + } return returnOptions; }; diff --git a/src/parsers/declarations.ts b/src/parsers/declarations.ts index 0e7f17c5..ba9f28e1 100644 --- a/src/parsers/declarations.ts +++ b/src/parsers/declarations.ts @@ -44,7 +44,8 @@ export const parseDeclarations = ( useCalc, stringMap, autoRename, - greedy + greedy, + aliases } = store.options; const deleteDeclarations: Declaration[] = []; @@ -132,7 +133,8 @@ export const parseDeclarations = ( stringMap, autoRename: autoRename !== Autorename.disabled, autoRenameStrict: autoRename === Autorename.strict, - greedy + greedy, + aliases }); const root = postcss.parse(declFlippedString); diff --git a/tests/__snapshots__/combined-aliases.test.ts.snap b/tests/__snapshots__/combined-aliases.test.ts.snap new file mode 100644 index 00000000..6660cb36 --- /dev/null +++ b/tests/__snapshots__/combined-aliases.test.ts.snap @@ -0,0 +1,107 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Aliases Tests Aliases default 1`] = ` +":root { + --small-padding: 2px 4px 8px 16px; + --large-padding: 4px 8px 16px 32px; + --custom-margin: 2px; + --small-margin: 2px 4px 8px 16px; + --large-margin: 4px 8px 16px 32px; +} + +.test1 { + margin: var(--small-margin); + padding: var(--small-padding); +} + +.test1.large { + margin: var(--large-margin); + padding: var(--large-padding); +} + +[dir=\\"ltr\\"] .test1.large { + left: 10px; +} + +[dir=\\"rtl\\"] .test1.large { + right: 10px; +} + +.test2 { + margin: var(--custom-margin); +}" +`; + +exports[`Aliases Tests Aliases map 1`] = ` +":root { + --custom-margin: 2px; +} + +[dir=\\"ltr\\"]:root { + --small-padding: 2px 4px 8px 16px; + --large-padding: 4px 8px 16px 32px; + --small-margin: 2px 4px 8px 16px; + --large-margin: 4px 8px 16px 32px; +} + +[dir=\\"rtl\\"]:root { + --small-padding: 2px 16px 8px 4px; + --large-padding: 4px 32px 16px 8px; + --small-margin: 2px 16px 8px 4px; + --large-margin: 4px 32px 16px 8px; +} + +.test1 { + margin: var(--small-margin); + padding: var(--small-padding); +} + +.test1.large { + margin: var(--large-margin); + padding: var(--large-padding); +} + +[dir=\\"ltr\\"] .test1.large { + left: 10px; +} + +[dir=\\"rtl\\"] .test1.large { + right: 10px; +} + +.test2 { + margin: var(--custom-margin); +}" +`; + +exports[`Aliases Tests Wrong aliases 1`] = ` +":root { + --small-padding: 2px 4px 8px 16px; + --large-padding: 4px 8px 16px 32px; + --custom-margin: 2px; + --small-margin: 2px 4px 8px 16px; + --large-margin: 4px 8px 16px 32px; +} + +.test1 { + margin: var(--small-margin); + padding: var(--small-padding); +} + +.test1.large { + margin: var(--large-margin); + padding: var(--large-padding); +} + +[dir=\\"ltr\\"] .test1.large { + left: 10px; +} + +[dir=\\"rtl\\"] .test1.large { + right: 10px; +} + +.test2 { + margin: var(--custom-margin); +}" +`; diff --git a/tests/__snapshots__/override-aliases.test.ts.snap b/tests/__snapshots__/override-aliases.test.ts.snap new file mode 100644 index 00000000..73f22274 --- /dev/null +++ b/tests/__snapshots__/override-aliases.test.ts.snap @@ -0,0 +1,98 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Aliases Tests Aliases default 1`] = ` +":root { + --small-padding: 2px 4px 8px 16px; + --large-padding: 4px 8px 16px 32px; + --custom-margin: 2px; + --small-margin: 2px 4px 8px 16px; + --large-margin: 4px 8px 16px 32px; +} + +.test1 { + margin: var(--small-margin); + padding: var(--small-padding); +} + +.test1.large { + margin: var(--large-margin); + padding: var(--large-padding); + left: 10px; +} + +[dir=\\"rtl\\"] .test1.large { + left: auto; + right: 10px; +} + +.test2 { + margin: var(--custom-margin); +}" +`; + +exports[`Aliases Tests Aliases map 1`] = ` +":root { + --small-padding: 2px 4px 8px 16px; + --large-padding: 4px 8px 16px 32px; + --custom-margin: 2px; + --small-margin: 2px 4px 8px 16px; + --large-margin: 4px 8px 16px 32px; +} + +[dir=\\"rtl\\"]:root { + --small-padding: 2px 16px 8px 4px; + --large-padding: 4px 32px 16px 8px; + --small-margin: 2px 16px 8px 4px; + --large-margin: 4px 32px 16px 8px; +} + +.test1 { + margin: var(--small-margin); + padding: var(--small-padding); +} + +.test1.large { + margin: var(--large-margin); + padding: var(--large-padding); + left: 10px; +} + +[dir=\\"rtl\\"] .test1.large { + left: auto; + right: 10px; +} + +.test2 { + margin: var(--custom-margin); +}" +`; + +exports[`Aliases Tests Wrong aliases 1`] = ` +":root { + --small-padding: 2px 4px 8px 16px; + --large-padding: 4px 8px 16px 32px; + --custom-margin: 2px; + --small-margin: 2px 4px 8px 16px; + --large-margin: 4px 8px 16px 32px; +} + +.test1 { + margin: var(--small-margin); + padding: var(--small-padding); +} + +.test1.large { + margin: var(--large-margin); + padding: var(--large-padding); + left: 10px; +} + +[dir=\\"rtl\\"] .test1.large { + left: auto; + right: 10px; +} + +.test2 { + margin: var(--custom-margin); +}" +`; diff --git a/tests/combined-aliases.test.ts b/tests/combined-aliases.test.ts new file mode 100644 index 00000000..9013619d --- /dev/null +++ b/tests/combined-aliases.test.ts @@ -0,0 +1,38 @@ +import postcss from 'postcss'; +import postcssRTLCSS from '../src'; +import { PluginOptions, Mode } from '../src/@types'; +import { readCSSFile } from './test-utils'; +import { aliases } from './tests-constants'; + +const baseOptions: PluginOptions = {mode: Mode.combined, aliases}; + +describe('Aliases Tests', (): void => { + + let input = ''; + + beforeEach(async (): Promise => { + input = input || await readCSSFile('input-variables.css'); + }); + + it('Aliases default', (): void => { + const options: PluginOptions = { ...baseOptions, aliases: {} }; + const output = postcss([postcssRTLCSS(options)]).process(input); + expect(output.css).toMatchSnapshot(); + expect(output.warnings()).toHaveLength(0); + }); + + it('Aliases map', (): void => { + const options: PluginOptions = { ...baseOptions }; + const output = postcss([postcssRTLCSS(options)]).process(input); + expect(output.css).toMatchSnapshot(); + expect(output.warnings()).toHaveLength(0); + }); + + it('Wrong aliases', (): void => { + const options: PluginOptions = { ...baseOptions, aliases: {base: true, parse: false} as unknown as Record }; + const output = postcss([postcssRTLCSS(options)]).process(input); + expect(output.css).toMatchSnapshot(); + expect(output.warnings()).toHaveLength(0); + }); + +}); \ No newline at end of file diff --git a/tests/css/input-variables.css b/tests/css/input-variables.css new file mode 100644 index 00000000..b56a2fda --- /dev/null +++ b/tests/css/input-variables.css @@ -0,0 +1,22 @@ +:root { + --small-padding: 2px 4px 8px 16px; + --large-padding: 4px 8px 16px 32px; + --custom-margin: 2px; + --small-margin: 2px 4px 8px 16px; + --large-margin: 4px 8px 16px 32px; +} + +.test1 { + margin: var(--small-margin); + padding: var(--small-padding); +} + +.test1.large { + margin: var(--large-margin); + padding: var(--large-padding); + left: 10px; +} + +.test2 { + margin: var(--custom-margin); +} \ No newline at end of file diff --git a/tests/override-aliases.test.ts b/tests/override-aliases.test.ts new file mode 100644 index 00000000..36e3ab0a --- /dev/null +++ b/tests/override-aliases.test.ts @@ -0,0 +1,38 @@ +import postcss from 'postcss'; +import postcssRTLCSS from '../src'; +import { PluginOptions, Mode } from '../src/@types'; +import { readCSSFile } from './test-utils'; +import { aliases } from './tests-constants'; + +const baseOptions: PluginOptions = {mode: Mode.override, aliases}; + +describe('Aliases Tests', (): void => { + + let input = ''; + + beforeEach(async (): Promise => { + input = input || await readCSSFile('input-variables.css'); + }); + + it('Aliases default', (): void => { + const options: PluginOptions = { ...baseOptions, aliases: {} }; + const output = postcss([postcssRTLCSS(options)]).process(input); + expect(output.css).toMatchSnapshot(); + expect(output.warnings()).toHaveLength(0); + }); + + it('Aliases map', (): void => { + const options: PluginOptions = { ...baseOptions }; + const output = postcss([postcssRTLCSS(options)]).process(input); + expect(output.css).toMatchSnapshot(); + expect(output.warnings()).toHaveLength(0); + }); + + it('Wrong aliases', (): void => { + const options: PluginOptions = { ...baseOptions, aliases: {base: true, parse: false} as unknown as Record }; + const output = postcss([postcssRTLCSS(options)]).process(input); + expect(output.css).toMatchSnapshot(); + expect(output.warnings()).toHaveLength(0); + }); + +}); \ No newline at end of file diff --git a/tests/tests-constants.ts b/tests/tests-constants.ts new file mode 100644 index 00000000..b9f275e5 --- /dev/null +++ b/tests/tests-constants.ts @@ -0,0 +1,10 @@ +const PADDING = 'padding'; +const MARGIN = 'margin'; + +export const aliases = { + '--small-padding': PADDING, + '--large-padding': PADDING, + '--custom-margin': MARGIN, + '--small-margin': MARGIN, + '--large-margin': MARGIN +}; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 30b124a5..7ab0a715 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4018,10 +4018,10 @@ rsvp@^4.8.4: resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== -rtlcss@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/rtlcss/-/rtlcss-3.1.2.tgz#4800d3d03525791a720f676a8ad2c6acf8efdfb2" - integrity sha512-b04YSX37siupPOWUEguEBReWX2w4QT89C0PI9g2JzZycbq7zrgPmTr1DA1pizSWpKRFdCjjnrx/SSvU4fOHmGg== +rtlcss@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/rtlcss/-/rtlcss-3.2.0.tgz#2139734ac45302891182f23f13448c51c07fccaf" + integrity sha512-nV3UmaTmA5TkP2dYOR16ULu6FkMOqZRbiXbFZnmWIN9coPfx3gin31VGOPV7vrVMPjNds7pCS2UYy0mwQUdFCQ== dependencies: chalk "^4.1.0" find-up "^5.0.0"