From 1899484ff238d9dc213f3e6b4f1999fb7375d8e8 Mon Sep 17 00:00:00 2001 From: Feoktist Shovchko Date: Tue, 16 Jul 2024 22:40:27 +0300 Subject: [PATCH 1/7] feat(esl-media-query): ingore tuple values if query syntax passed --- .../4/deprecated.media-rule-list-parse.ts | 3 +- src/modules/esl-media-query/README.md | 12 +++--- .../core/esl-media-rule-list.ts | 36 +++++++---------- .../test/esl-media-rule-list.test.ts | 40 ++++++++++++++++--- 4 files changed, 58 insertions(+), 33 deletions(-) diff --git a/eslint/src/rules/4/deprecated.media-rule-list-parse.ts b/eslint/src/rules/4/deprecated.media-rule-list-parse.ts index 2dc15e368..cc4ba6ac0 100644 --- a/eslint/src/rules/4/deprecated.media-rule-list-parse.ts +++ b/eslint/src/rules/4/deprecated.media-rule-list-parse.ts @@ -8,11 +8,12 @@ import type {ESLintReplacementMethodCfg} from '../../core/deprecated-class-metho const AVAILABLE_SINCE = '5.0.0-beta.24'; const isActual = lte(ESL_PACKAGE_VERSION, AVAILABLE_SINCE); +const isTest = process.env.NODE_ENV === 'test'; /** * Rule for deprecated 'parse' method of {@link ESLMediaRuleList} */ -export default isActual ? +export default isActual || isTest ? buildRule({ className: 'ESLMediaRuleList', deprecatedMethod: 'parse', diff --git a/src/modules/esl-media-query/README.md b/src/modules/esl-media-query/README.md index 1f990f131..e724d87dc 100644 --- a/src/modules/esl-media-query/README.md +++ b/src/modules/esl-media-query/README.md @@ -204,20 +204,22 @@ ESLMediaRuleList.parse('1 | @XS => 2', String); // the same as sample above ESLMediaRuleList.parse('1 | @XS => 2', Number); // first query from the sample above that store numeric values ESLMediaRuleList.parse('@XS => {option: 1} | @+SM => {option: 2}', ESLMediaRuleList.OBJECT_PARSER); // second query from the sample above with an object payloads ESLMediaRuleList.parse('@XS => {option: 1} | @+SM => {option: 2}', evaluate); // the same as the sample above +ESLMediaRuleList.parse('1|2|3|4|5', '@xs|@sm|@md|@lg|@xl') // parses tuple payloads // Tupple parsing ESLMediaRuleList.parseTuple('@xs|@sm|@md|@lg|@xl', '1|2|3|4|5') // String payload example ESLMediaRuleList.parseTuple('@xs|@sm|@md|@lg|@xl', '1|2|3|4|5', Number) // Numeric payload sample ``` -**Note**: Method `ESLMediaRuleList.parse` is deprecated, and will be reintroduced in ESL v5.0.0 with a different signature. For now use `ESLMediaRuleList.parseTuple` or `ESLMediaRuleList.parseQuery` instead. #### ESLMediaRuleList API -- `ESLMediaRuleList.parse(ruleset: string)` - parse media ruleset defined with classic syntax mentioned in section above. -Rules separated by `|` symbol, query and value separated by `=>` for each rule, query is optional. +- `ESLMediaRuleList.parseTuple(queries: string, values: string)` - parse media ruleset from tuple of queries and values, all separated via `|` symbol. -- `ESLMediaRuleList.parseTuple(queries: string, values: string)` - parse media ruleset from tuple of -queries and values, all separated via `|` symbol +- `ESLMediaRuleList.parseQuery(ruleset: string)` - parse media ruleset defined with syntax, where query and value are a single argument separated by `=>` for each rule. + +- `ESLMediaRuleList.parse(values: string, queries: string)` - parse media ruleset defined with tuple syntax, where query and value are separate arguments. + +- `ESLMediaRuleList.parse(ruleset: string)` - parse media ruleset defined with same syntax as `ESLMediaRuleList.parseQuery`. - `ESLMediaRuleList.prototype.rules` - array of rules that defines `ESLMediaRuleList` object - `ESLMediaRuleList.prototype.active` - array of active (matched) rules diff --git a/src/modules/esl-media-query/core/esl-media-rule-list.ts b/src/modules/esl-media-query/core/esl-media-rule-list.ts index 1831102f1..9880b6a8a 100644 --- a/src/modules/esl-media-query/core/esl-media-rule-list.ts +++ b/src/modules/esl-media-query/core/esl-media-rule-list.ts @@ -35,9 +35,7 @@ export class ESLMediaRuleList extends SyntheticEventTarget { public static OBJECT_PARSER = (val: string): U | undefined => evaluate(val); /** - * @deprecated Method will be reintroduced in v5.0.0 with a different signature. For now use ESLMediaRuleList.parseQuery instead - * - * Creates `ESLMediaRuleList` from string query representation + * Creates {@link ESLMediaRuleList} from string query representation * Expect serialized {@link ESLMediaRule}s separated by '|' * Uses exact strings as rule list values * @@ -45,9 +43,7 @@ export class ESLMediaRuleList extends SyntheticEventTarget { */ public static parse(query: string): ESLMediaRuleList; /** - * @deprecated Method will be reintroduced in v5.0.0 with a different signature. For now use ESLMediaRuleList.parseQuery instead - * - * Creates `ESLMediaRuleList` from string query representation. + * Creates {@link ESLMediaRuleList} from string query representation. * Expect serialized {@link ESLMediaRule}s separated by '|' * * @param query - query string @@ -55,26 +51,22 @@ export class ESLMediaRuleList extends SyntheticEventTarget { */ public static parse(query: string, parser: RulePayloadParser): ESLMediaRuleList; /** - * @deprecated Method will be reintroduced in v5.0.0 with a different signature. For now use ESLMediaRuleList.parseTuple instead - - * Creates `ESLMediaRuleList` from two strings with conditions and values sequences + * Creates {@link ESLMediaRuleList} from two strings with conditions and values sequences * - * @param mask - media conditions tuple string (uses '|' as separator) * @param values - values tuple string (uses '|' as separator) + * @param mask - media conditions tuple string (uses '|' as separator) * * @example * ```ts * ESLMediaRuleList.parse('@XS|@SM|@MD|@LG|@XL', '1|2|3|4|5') * ``` */ - public static parse(mask: string, values: string): ESLMediaRuleList; + public static parse(values: string, mask: string): ESLMediaRuleList; /** - * @deprecated Method will be reintroduced in v5.0.0 with a different signature. For now use ESLMediaRuleList.parseTuple instead - * - * Creates `ESLMediaRuleList` from two strings with conditions and values sequences + * Creates {@link ESLMediaRuleList} from two strings with conditions and values sequences * - * @param mask - media conditions tuple string (uses '|' as separator) * @param values - values tuple string (uses '|' as separator) + * @param mask - media conditions tuple string (uses '|' as separator) * @param parser - value parser function * * @example @@ -82,17 +74,17 @@ export class ESLMediaRuleList extends SyntheticEventTarget { * ESLMediaRuleList.parseTuple('@XS|@SM|@MD|@LG|@XL', '1|2|3|4|5', Number) * ``` */ - public static parse(mask: string, values: string, parser: RulePayloadParser): ESLMediaRuleList; + public static parse(values: string, mask: string, parser: RulePayloadParser): ESLMediaRuleList; public static parse(query: string, ...common: (string | RulePayloadParser)[]): ESLMediaRuleList { const parser: RulePayloadParser = typeof common[common.length - 1] === 'function' ? common.pop() as any : String; - const value = common.pop(); - return typeof value === 'string' ? - ESLMediaRuleList.parseTuple(query, value, parser) : + const mask = common.pop(); + return typeof mask === 'string' ? + ESLMediaRuleList.parseTuple(mask, query, parser) : ESLMediaRuleList.parseQuery(query, parser); } /** - * Creates `ESLMediaRuleList` from string query representation + * Creates {@link ESLMediaRuleList} from string query representation * Uses exact strings as rule list values * @param query - query string */ @@ -111,7 +103,7 @@ export class ESLMediaRuleList extends SyntheticEventTarget { } /** - * Creates `ESLMediaRuleList` from two strings with conditions and values sequences + * Creates {@link ESLMediaRuleList} from two strings with conditions and values sequences * * @param mask - media conditions tuple string (uses '|' as separator) * @param values - values tuple string (uses '|' as separator) @@ -123,7 +115,7 @@ export class ESLMediaRuleList extends SyntheticEventTarget { */ public static parseTuple(mask: string, values: string): ESLMediaRuleList; /** - * Creates `ESLMediaRuleList` from two strings with conditions and values sequences + * Creates {@link ESLMediaRuleList} from two strings with conditions and values sequences * * @param mask - media conditions tuple string (uses '|' as separator) * @param values - values tuple string (uses '|' as separator) diff --git a/src/modules/esl-media-query/test/esl-media-rule-list.test.ts b/src/modules/esl-media-query/test/esl-media-rule-list.test.ts index cce13af4d..37bbb20a5 100644 --- a/src/modules/esl-media-query/test/esl-media-rule-list.test.ts +++ b/src/modules/esl-media-query/test/esl-media-rule-list.test.ts @@ -8,7 +8,7 @@ describe('ESLMediaRuleList', () => { describe('Integration cases:', () => { test('Basic case: "1 | @sm => 2 | @md => 3" parsed correctly', () => { - const mrl = ESLMediaRuleList.parseQuery('1 | @sm => 2 | @md => 3'); + const mrl = ESLMediaRuleList.parse('1 | @sm => 2 | @md => 3'); expect(mrl.rules.length).toBe(3); mockSmMatchMedia.matches = false; @@ -57,7 +57,7 @@ describe('ESLMediaRuleList', () => { }); test('Extended media case parsed correctly: "1 | @sm or @md => 2"', () => { - const mrl = ESLMediaRuleList.parseQuery('1 | @sm or @md => 2'); + const mrl = ESLMediaRuleList.parse('1 | @sm or @md => 2'); const listener = jest.fn(); expect(mrl.rules.length).toBe(2); @@ -112,7 +112,7 @@ describe('ESLMediaRuleList', () => { describe('Basic cases:', () => { test('Single value parsed to the single "all" rule', () => { - const mrl = ESLMediaRuleList.parseQuery('123'); + const mrl = ESLMediaRuleList.parse('123'); expect(mrl.rules.length).toBe(1); expect(mrl.active.length).toBeGreaterThan(0); expect(mrl.value).toBe('123'); @@ -120,12 +120,12 @@ describe('ESLMediaRuleList', () => { }); test('Single rule with media query "@sm => 1"', () => { - const mrl = ESLMediaRuleList.parseQuery('@sm => 1'); + const mrl = ESLMediaRuleList.parse('@sm => 1'); expect(mrl.rules.length).toBe(1); }); test('Single rule "@sm => 1" response to the matcher correctly', () => { - const mrl = ESLMediaRuleList.parseQuery('@sm => 1'); + const mrl = ESLMediaRuleList.parse('@sm => 1'); mockSmMatchMedia.matches = false; expect(mrl.value).toBe(undefined); @@ -178,4 +178,34 @@ describe('ESLMediaRuleList', () => { expect(() => ESLMediaRuleList.parseTuple('@xs', '1|2|3')).toThrowError(); }); }); + + describe('Adaptive cases parsing', () => { + test('Should throw error if arrow syntax provided instead of tuple\'s mask', () => { + try { + ESLMediaRuleList.parse('(min-width: 100px) => 123', '3|4'); + } catch (e) { + expect(e.toString()).toBe('Error: Value doesn\'t correspond to mask'); + } + }); + + test('Handle tuple with equal mask/value pair as regular', () => { + const mask = '@sm|@md|@lg'; + const values = '1|2|3'; + const mrl = ESLMediaRuleList.parse(values, mask); + expect(mrl.toString()).toBe(ESLMediaRuleList.parseTuple(mask, values).toString()); + expect(mrl.toString()).toBe( + '(min-width: 768px) and (max-width: 991px) => 1|(min-width: 992px) and (max-width: 1199px) => 2|(min-width: 1200px) and (max-width: 1599px) => 3'); + }); + + + test.each([ + [['1'], 'all => 1'], + [['0 | (min-width: 100px) => 1 | (min-width: 200px) => 2'], 'all => 0|(min-width: 100px) => 1|(min-width: 200px) => 2'], + [['0 | 1', '@+xs | @+sm'], '(min-width: 1px) => 0|(min-width: 768px) => 1'], + [['1|2|3', 'all|@sm|@md'], 'all => 1|(min-width: 768px) and (max-width: 991px) => 2|(min-width: 992px) and (max-width: 1199px) => 3'], + [['1 | @+sm => 2 | @+md => 3'], 'all => 1|(min-width: 768px) => 2|(min-width: 992px) => 3'], + ])('Should correctly parse "%s" with media condition "%s"', (params: any[], canonical: string) => { + expect(ESLMediaRuleList.parse.apply(null, params).toString()).toBe(canonical); + }); + }); }); From d0e2587a7d31980f4894f1f5065ba98df5f31780 Mon Sep 17 00:00:00 2001 From: Feoktist Shovchko Date: Wed, 17 Jul 2024 10:44:04 +0300 Subject: [PATCH 2/7] chore(esl-media-query): code refactoring --- src/modules/esl-media-query/README.md | 5 ++-- .../core/esl-media-rule-list.ts | 1 + .../test/esl-media-rule-list.test.ts | 25 +++++++++---------- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/modules/esl-media-query/README.md b/src/modules/esl-media-query/README.md index e724d87dc..ec28ad764 100644 --- a/src/modules/esl-media-query/README.md +++ b/src/modules/esl-media-query/README.md @@ -205,6 +205,7 @@ ESLMediaRuleList.parse('1 | @XS => 2', Number); // first query from the sample a ESLMediaRuleList.parse('@XS => {option: 1} | @+SM => {option: 2}', ESLMediaRuleList.OBJECT_PARSER); // second query from the sample above with an object payloads ESLMediaRuleList.parse('@XS => {option: 1} | @+SM => {option: 2}', evaluate); // the same as the sample above ESLMediaRuleList.parse('1|2|3|4|5', '@xs|@sm|@md|@lg|@xl') // parses tuple payloads +ESLMediaRuleList.parse('@mqAlt1 => value1| @mqAlt2 => value2', '@mq1|@mq2'); // parses first argument as an arrow syntax without default tuple consideration. // Tupple parsing ESLMediaRuleList.parseTuple('@xs|@sm|@md|@lg|@xl', '1|2|3|4|5') // String payload example @@ -217,9 +218,7 @@ ESLMediaRuleList.parseTuple('@xs|@sm|@md|@lg|@xl', '1|2|3|4|5', Number) // Nume - `ESLMediaRuleList.parseQuery(ruleset: string)` - parse media ruleset defined with syntax, where query and value are a single argument separated by `=>` for each rule. -- `ESLMediaRuleList.parse(values: string, queries: string)` - parse media ruleset defined with tuple syntax, where query and value are separate arguments. - -- `ESLMediaRuleList.parse(ruleset: string)` - parse media ruleset defined with same syntax as `ESLMediaRuleList.parseQuery`. +- `ESLMediaRuleList.parse(values: string, queries: string)` - parse media ruleset adaptively. Method can support both notations in case if query syntax separated by `=>` is being used as a value for a tuple tuple. - `ESLMediaRuleList.prototype.rules` - array of rules that defines `ESLMediaRuleList` object - `ESLMediaRuleList.prototype.active` - array of active (matched) rules diff --git a/src/modules/esl-media-query/core/esl-media-rule-list.ts b/src/modules/esl-media-query/core/esl-media-rule-list.ts index 9880b6a8a..8bdab54b9 100644 --- a/src/modules/esl-media-query/core/esl-media-rule-list.ts +++ b/src/modules/esl-media-query/core/esl-media-rule-list.ts @@ -77,6 +77,7 @@ export class ESLMediaRuleList extends SyntheticEventTarget { public static parse(values: string, mask: string, parser: RulePayloadParser): ESLMediaRuleList; public static parse(query: string, ...common: (string | RulePayloadParser)[]): ESLMediaRuleList { const parser: RulePayloadParser = typeof common[common.length - 1] === 'function' ? common.pop() as any : String; + if (query.includes('=>')) return ESLMediaRuleList.parseQuery(query, parser); const mask = common.pop(); return typeof mask === 'string' ? ESLMediaRuleList.parseTuple(mask, query, parser) : diff --git a/src/modules/esl-media-query/test/esl-media-rule-list.test.ts b/src/modules/esl-media-query/test/esl-media-rule-list.test.ts index 37bbb20a5..6cbda8433 100644 --- a/src/modules/esl-media-query/test/esl-media-rule-list.test.ts +++ b/src/modules/esl-media-query/test/esl-media-rule-list.test.ts @@ -8,7 +8,7 @@ describe('ESLMediaRuleList', () => { describe('Integration cases:', () => { test('Basic case: "1 | @sm => 2 | @md => 3" parsed correctly', () => { - const mrl = ESLMediaRuleList.parse('1 | @sm => 2 | @md => 3'); + const mrl = ESLMediaRuleList.parseQuery('1 | @sm => 2 | @md => 3'); expect(mrl.rules.length).toBe(3); mockSmMatchMedia.matches = false; @@ -57,7 +57,7 @@ describe('ESLMediaRuleList', () => { }); test('Extended media case parsed correctly: "1 | @sm or @md => 2"', () => { - const mrl = ESLMediaRuleList.parse('1 | @sm or @md => 2'); + const mrl = ESLMediaRuleList.parseQuery('1 | @sm or @md => 2'); const listener = jest.fn(); expect(mrl.rules.length).toBe(2); @@ -112,7 +112,7 @@ describe('ESLMediaRuleList', () => { describe('Basic cases:', () => { test('Single value parsed to the single "all" rule', () => { - const mrl = ESLMediaRuleList.parse('123'); + const mrl = ESLMediaRuleList.parseQuery('123'); expect(mrl.rules.length).toBe(1); expect(mrl.active.length).toBeGreaterThan(0); expect(mrl.value).toBe('123'); @@ -120,12 +120,12 @@ describe('ESLMediaRuleList', () => { }); test('Single rule with media query "@sm => 1"', () => { - const mrl = ESLMediaRuleList.parse('@sm => 1'); + const mrl = ESLMediaRuleList.parseQuery('@sm => 1'); expect(mrl.rules.length).toBe(1); }); test('Single rule "@sm => 1" response to the matcher correctly', () => { - const mrl = ESLMediaRuleList.parse('@sm => 1'); + const mrl = ESLMediaRuleList.parseQuery('@sm => 1'); mockSmMatchMedia.matches = false; expect(mrl.value).toBe(undefined); @@ -180,15 +180,14 @@ describe('ESLMediaRuleList', () => { }); describe('Adaptive cases parsing', () => { - test('Should throw error if arrow syntax provided instead of tuple\'s mask', () => { - try { - ESLMediaRuleList.parse('(min-width: 100px) => 123', '3|4'); - } catch (e) { - expect(e.toString()).toBe('Error: Value doesn\'t correspond to mask'); - } + test('Should ignore second tuple parameter if arrow syntax was detected in a first one', () => { + const query = '(min-width: 100px) => 123'; + const mrl = ESLMediaRuleList.parse(query, '3|4'); + expect(mrl.toString()).toBe(ESLMediaRuleList.parseQuery(query).toString()); + expect(mrl.toString()).toBe('(min-width: 100px) => 123'); }); - test('Handle tuple with equal mask/value pair as regular', () => { + test('Handle tuple with equal mask/value pair as regular, but with arguments that have flipped positions', () => { const mask = '@sm|@md|@lg'; const values = '1|2|3'; const mrl = ESLMediaRuleList.parse(values, mask); @@ -197,12 +196,12 @@ describe('ESLMediaRuleList', () => { '(min-width: 768px) and (max-width: 991px) => 1|(min-width: 992px) and (max-width: 1199px) => 2|(min-width: 1200px) and (max-width: 1599px) => 3'); }); - test.each([ [['1'], 'all => 1'], [['0 | (min-width: 100px) => 1 | (min-width: 200px) => 2'], 'all => 0|(min-width: 100px) => 1|(min-width: 200px) => 2'], [['0 | 1', '@+xs | @+sm'], '(min-width: 1px) => 0|(min-width: 768px) => 1'], [['1|2|3', 'all|@sm|@md'], 'all => 1|(min-width: 768px) and (max-width: 991px) => 2|(min-width: 992px) and (max-width: 1199px) => 3'], + [['1 | @+sm => 2', '@sm'], 'all => 1|(min-width: 768px) => 2'], [['1 | @+sm => 2 | @+md => 3'], 'all => 1|(min-width: 768px) => 2|(min-width: 992px) => 3'], ])('Should correctly parse "%s" with media condition "%s"', (params: any[], canonical: string) => { expect(ESLMediaRuleList.parse.apply(null, params).toString()).toBe(canonical); From add038afec8cd9b09e2ac4b8dcfee019c6dfea9a Mon Sep 17 00:00:00 2001 From: Feoktist Shovchko Date: Wed, 17 Jul 2024 10:56:36 +0300 Subject: [PATCH 3/7] chore(esl-media-query): docs refactoring --- src/modules/esl-media-query/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/esl-media-query/README.md b/src/modules/esl-media-query/README.md index ec28ad764..e8390f0ef 100644 --- a/src/modules/esl-media-query/README.md +++ b/src/modules/esl-media-query/README.md @@ -218,7 +218,7 @@ ESLMediaRuleList.parseTuple('@xs|@sm|@md|@lg|@xl', '1|2|3|4|5', Number) // Nume - `ESLMediaRuleList.parseQuery(ruleset: string)` - parse media ruleset defined with syntax, where query and value are a single argument separated by `=>` for each rule. -- `ESLMediaRuleList.parse(values: string, queries: string)` - parse media ruleset adaptively. Method can support both notations in case if query syntax separated by `=>` is being used as a value for a tuple tuple. +- `ESLMediaRuleList.parse(values: string, queries: string)` - parse media ruleset adaptively. Method can support both notations with a ruleset from tuple of queries and values, all separated via `|` symbol, and can parse cases where query syntax with rules separated by `=>` is being used as a value for a tuple. - `ESLMediaRuleList.prototype.rules` - array of rules that defines `ESLMediaRuleList` object - `ESLMediaRuleList.prototype.active` - array of active (matched) rules From bd764b24622dabe1d7c387dde9ec85270cd8c749 Mon Sep 17 00:00:00 2001 From: Feoktist Shovchko Date: Wed, 17 Jul 2024 14:31:25 +0300 Subject: [PATCH 4/7] chore(esl-media-query): drop `ESLMediaRuleList.parse` lint rule --- eslint/src/rules/4/all.rules.ts | 3 --- .../rules/4/deprecated.media-rule-list-parse.ts | 3 +-- eslint/test/deprecated-class-method.test.ts | 15 +++++++++++++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/eslint/src/rules/4/all.rules.ts b/eslint/src/rules/4/all.rules.ts index ead4003a9..f39092f3a 100644 --- a/eslint/src/rules/4/all.rules.ts +++ b/eslint/src/rules/4/all.rules.ts @@ -6,8 +6,6 @@ import deprecatedToggleableActionParams from './deprecated.toggleable-action-par import deprecatedBaseDecoratorsPath from './deprecated.base-decorators-path'; -import deprecatedMediaRuleListParse from './deprecated.media-rule-list-parse'; - export default { // Aliases 'deprecated-4/generate-uid': deprecatedGenerateUid, @@ -15,7 +13,6 @@ export default { 'deprecated-4/event-utils': deprecatedEventUtils, 'deprecated-4/traversing-query': deprecatedTraversingQuery, 'deprecated-4/toggleable-action-params': deprecatedToggleableActionParams, - 'deprecated-4/media-rule-list-parse': deprecatedMediaRuleListParse, // Paths 'deprecated-4/base-decorators-path': deprecatedBaseDecoratorsPath }; diff --git a/eslint/src/rules/4/deprecated.media-rule-list-parse.ts b/eslint/src/rules/4/deprecated.media-rule-list-parse.ts index cc4ba6ac0..2dc15e368 100644 --- a/eslint/src/rules/4/deprecated.media-rule-list-parse.ts +++ b/eslint/src/rules/4/deprecated.media-rule-list-parse.ts @@ -8,12 +8,11 @@ import type {ESLintReplacementMethodCfg} from '../../core/deprecated-class-metho const AVAILABLE_SINCE = '5.0.0-beta.24'; const isActual = lte(ESL_PACKAGE_VERSION, AVAILABLE_SINCE); -const isTest = process.env.NODE_ENV === 'test'; /** * Rule for deprecated 'parse' method of {@link ESLMediaRuleList} */ -export default isActual || isTest ? +export default isActual ? buildRule({ className: 'ESLMediaRuleList', deprecatedMethod: 'parse', diff --git a/eslint/test/deprecated-class-method.test.ts b/eslint/test/deprecated-class-method.test.ts index bbc4cc217..944e7cf7e 100644 --- a/eslint/test/deprecated-class-method.test.ts +++ b/eslint/test/deprecated-class-method.test.ts @@ -1,7 +1,7 @@ import {RuleTester} from 'eslint'; import {buildRule} from '../src/core/deprecated-class-method'; -import deprecatedMediaRuleListParse from '../src/rules/4/deprecated.media-rule-list-parse'; +import type * as ESTree from 'estree'; const VALID_CASES = [ { @@ -189,7 +189,18 @@ describe('ESL Migration Rules: Deprecated Static Method: valid', () => { }); describe('ESL Migration Rules: Deprecated Static Method: valid', () => { - const rule = deprecatedMediaRuleListParse; + const rule = buildRule({ + className: 'ESLMediaRuleList', + deprecatedMethod: 'parse', + getReplacementMethod: (expression) => { + const args = expression.arguments; + const isLiteral = (node: ESTree.Expression | ESTree.SpreadElement): boolean => node?.type === 'Literal' || node?.type === 'TemplateLiteral'; + if (expression.type === 'CallExpression' && args.length === 1) return 'parseQuery'; + if (expression.type === 'CallExpression' && args.length === 2 && isLiteral(args[1])) return 'parseTuple'; + if (expression.type === 'CallExpression' && args.length === 3) return 'parseTuple'; + return {message: 'ESLMediaRuleList.parseQuery or ESLMediaRuleList.parseTuple'}; + } + }); const ruleTester = new RuleTester({ parser: require.resolve('@typescript-eslint/parser') From c95396d0f4a0894715d92e273b719dc69e473a4e Mon Sep 17 00:00:00 2001 From: "ala'n (Alexey Stsefanovich)" Date: Thu, 18 Jul 2024 17:58:32 +0200 Subject: [PATCH 5/7] Revert "chore(esl-media-query): drop `ESLMediaRuleList.parse` lint rule" This reverts commit bd764b24622dabe1d7c387dde9ec85270cd8c749. --- eslint/src/rules/4/all.rules.ts | 3 +++ .../rules/4/deprecated.media-rule-list-parse.ts | 3 ++- eslint/test/deprecated-class-method.test.ts | 15 ++------------- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/eslint/src/rules/4/all.rules.ts b/eslint/src/rules/4/all.rules.ts index f39092f3a..ead4003a9 100644 --- a/eslint/src/rules/4/all.rules.ts +++ b/eslint/src/rules/4/all.rules.ts @@ -6,6 +6,8 @@ import deprecatedToggleableActionParams from './deprecated.toggleable-action-par import deprecatedBaseDecoratorsPath from './deprecated.base-decorators-path'; +import deprecatedMediaRuleListParse from './deprecated.media-rule-list-parse'; + export default { // Aliases 'deprecated-4/generate-uid': deprecatedGenerateUid, @@ -13,6 +15,7 @@ export default { 'deprecated-4/event-utils': deprecatedEventUtils, 'deprecated-4/traversing-query': deprecatedTraversingQuery, 'deprecated-4/toggleable-action-params': deprecatedToggleableActionParams, + 'deprecated-4/media-rule-list-parse': deprecatedMediaRuleListParse, // Paths 'deprecated-4/base-decorators-path': deprecatedBaseDecoratorsPath }; diff --git a/eslint/src/rules/4/deprecated.media-rule-list-parse.ts b/eslint/src/rules/4/deprecated.media-rule-list-parse.ts index 2dc15e368..cc4ba6ac0 100644 --- a/eslint/src/rules/4/deprecated.media-rule-list-parse.ts +++ b/eslint/src/rules/4/deprecated.media-rule-list-parse.ts @@ -8,11 +8,12 @@ import type {ESLintReplacementMethodCfg} from '../../core/deprecated-class-metho const AVAILABLE_SINCE = '5.0.0-beta.24'; const isActual = lte(ESL_PACKAGE_VERSION, AVAILABLE_SINCE); +const isTest = process.env.NODE_ENV === 'test'; /** * Rule for deprecated 'parse' method of {@link ESLMediaRuleList} */ -export default isActual ? +export default isActual || isTest ? buildRule({ className: 'ESLMediaRuleList', deprecatedMethod: 'parse', diff --git a/eslint/test/deprecated-class-method.test.ts b/eslint/test/deprecated-class-method.test.ts index 944e7cf7e..bbc4cc217 100644 --- a/eslint/test/deprecated-class-method.test.ts +++ b/eslint/test/deprecated-class-method.test.ts @@ -1,7 +1,7 @@ import {RuleTester} from 'eslint'; import {buildRule} from '../src/core/deprecated-class-method'; -import type * as ESTree from 'estree'; +import deprecatedMediaRuleListParse from '../src/rules/4/deprecated.media-rule-list-parse'; const VALID_CASES = [ { @@ -189,18 +189,7 @@ describe('ESL Migration Rules: Deprecated Static Method: valid', () => { }); describe('ESL Migration Rules: Deprecated Static Method: valid', () => { - const rule = buildRule({ - className: 'ESLMediaRuleList', - deprecatedMethod: 'parse', - getReplacementMethod: (expression) => { - const args = expression.arguments; - const isLiteral = (node: ESTree.Expression | ESTree.SpreadElement): boolean => node?.type === 'Literal' || node?.type === 'TemplateLiteral'; - if (expression.type === 'CallExpression' && args.length === 1) return 'parseQuery'; - if (expression.type === 'CallExpression' && args.length === 2 && isLiteral(args[1])) return 'parseTuple'; - if (expression.type === 'CallExpression' && args.length === 3) return 'parseTuple'; - return {message: 'ESLMediaRuleList.parseQuery or ESLMediaRuleList.parseTuple'}; - } - }); + const rule = deprecatedMediaRuleListParse; const ruleTester = new RuleTester({ parser: require.resolve('@typescript-eslint/parser') From 693b84da3b46e68ab6349d5d768f8652caf44b25 Mon Sep 17 00:00:00 2001 From: "ala'n (Alexey Stsefanovich)" Date: Thu, 18 Jul 2024 18:00:10 +0200 Subject: [PATCH 6/7] style(esl-media-query): extend tests, stylistic fixes --- .../core/esl-media-rule-list.ts | 8 ++-- .../test/esl-media-rule-list.test.ts | 38 +++++++++---------- 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/src/modules/esl-media-query/core/esl-media-rule-list.ts b/src/modules/esl-media-query/core/esl-media-rule-list.ts index 8bdab54b9..0b8bd7b29 100644 --- a/src/modules/esl-media-query/core/esl-media-rule-list.ts +++ b/src/modules/esl-media-query/core/esl-media-rule-list.ts @@ -77,11 +77,9 @@ export class ESLMediaRuleList extends SyntheticEventTarget { public static parse(values: string, mask: string, parser: RulePayloadParser): ESLMediaRuleList; public static parse(query: string, ...common: (string | RulePayloadParser)[]): ESLMediaRuleList { const parser: RulePayloadParser = typeof common[common.length - 1] === 'function' ? common.pop() as any : String; - if (query.includes('=>')) return ESLMediaRuleList.parseQuery(query, parser); const mask = common.pop(); - return typeof mask === 'string' ? - ESLMediaRuleList.parseTuple(mask, query, parser) : - ESLMediaRuleList.parseQuery(query, parser); + if (query.includes('=>') || typeof mask !== 'string') return ESLMediaRuleList.parseQuery(query, parser); + return ESLMediaRuleList.parseTuple(mask, query, parser); } /** @@ -209,7 +207,7 @@ export class ESLMediaRuleList extends SyntheticEventTarget { /** @returns serialized {@link ESLMediaRuleList} object representation*/ public override toString(): string { - return this.rules.join('|'); + return this.rules.join(' | '); } } diff --git a/src/modules/esl-media-query/test/esl-media-rule-list.test.ts b/src/modules/esl-media-query/test/esl-media-rule-list.test.ts index 6cbda8433..192ad729b 100644 --- a/src/modules/esl-media-query/test/esl-media-rule-list.test.ts +++ b/src/modules/esl-media-query/test/esl-media-rule-list.test.ts @@ -180,29 +180,25 @@ describe('ESLMediaRuleList', () => { }); describe('Adaptive cases parsing', () => { - test('Should ignore second tuple parameter if arrow syntax was detected in a first one', () => { - const query = '(min-width: 100px) => 123'; - const mrl = ESLMediaRuleList.parse(query, '3|4'); - expect(mrl.toString()).toBe(ESLMediaRuleList.parseQuery(query).toString()); - expect(mrl.toString()).toBe('(min-width: 100px) => 123'); - }); - - test('Handle tuple with equal mask/value pair as regular, but with arguments that have flipped positions', () => { - const mask = '@sm|@md|@lg'; - const values = '1|2|3'; - const mrl = ESLMediaRuleList.parse(values, mask); - expect(mrl.toString()).toBe(ESLMediaRuleList.parseTuple(mask, values).toString()); - expect(mrl.toString()).toBe( - '(min-width: 768px) and (max-width: 991px) => 1|(min-width: 992px) and (max-width: 1199px) => 2|(min-width: 1200px) and (max-width: 1599px) => 3'); - }); - test.each([ + // [ [.. Call Args], 'Canonical form'] [['1'], 'all => 1'], - [['0 | (min-width: 100px) => 1 | (min-width: 200px) => 2'], 'all => 0|(min-width: 100px) => 1|(min-width: 200px) => 2'], - [['0 | 1', '@+xs | @+sm'], '(min-width: 1px) => 0|(min-width: 768px) => 1'], - [['1|2|3', 'all|@sm|@md'], 'all => 1|(min-width: 768px) and (max-width: 991px) => 2|(min-width: 992px) and (max-width: 1199px) => 3'], - [['1 | @+sm => 2', '@sm'], 'all => 1|(min-width: 768px) => 2'], - [['1 | @+sm => 2 | @+md => 3'], 'all => 1|(min-width: 768px) => 2|(min-width: 992px) => 3'], + + // Tuples + [['1', '@-sm'], '(max-width: 991px) => 1'], + [['1', '@+sm'], '(min-width: 768px) => 1'], + [['1', '@sm'], '(min-width: 768px) and (max-width: 991px) => 1'], + [['0 | 1', '@+xs | @+sm'], '(min-width: 1px) => 0 | (min-width: 768px) => 1'], + [['1|2|3', 'all|@sm|@md'], 'all => 1 | (min-width: 768px) and (max-width: 991px) => 2 | (min-width: 992px) and (max-width: 1199px) => 3'], + [['f=|s>|t<', 'all | @-xs | @-sm',], 'all => f= | (max-width: 767px) => s> | (max-width: 991px) => t<'], + + // Arrow syntax + [['0 | (min-width: 100px) => 1 | (min-width: 200px) => 2'], 'all => 0 | (min-width: 100px) => 1 | (min-width: 200px) => 2'], + [['1 | @+sm => 2 | @+md => 3'], 'all => 1 | (min-width: 768px) => 2 | (min-width: 992px) => 3'], + [['1 | @+sm => 2', '@sm'], 'all => 1 | (min-width: 768px) => 2'], + [['1 | screen and @sm => 2'], 'all => 1 | screen and (min-width: 768px) and (max-width: 991px) => 2'], + [['1 | @-xs or @+md => 2'], 'all => 1 | (max-width: 767px), (min-width: 992px) => 2'], + [['@-xs => hello | @+sm => world'], '(max-width: 767px) => hello | (min-width: 768px) => world'], ])('Should correctly parse "%s" with media condition "%s"', (params: any[], canonical: string) => { expect(ESLMediaRuleList.parse.apply(null, params).toString()).toBe(canonical); }); From 025166ef45b8614b1908a11f1afbdd61edb0b4be Mon Sep 17 00:00:00 2001 From: "ala'n (Alexey Stsefanovich)" Date: Thu, 18 Jul 2024 18:06:32 +0200 Subject: [PATCH 7/7] chore(esl-media-query): exclude `@exadel/esl/deprecated-4/media-rule-list-parse` rule for current snapshot state --- .eslintrc.yml | 3 +++ eslint/src/rules/4/deprecated.media-rule-list-parse.ts | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index eaf170754..0ed717b16 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -51,3 +51,6 @@ rules: 'editorconfig/eol-last': warn # Require no trailing spaces 'editorconfig/no-trailing-spaces': warn + + # Exclude migration rules for 5.0.0-beta.24 revision + '@exadel/esl/deprecated-4/media-rule-list-parse': off diff --git a/eslint/src/rules/4/deprecated.media-rule-list-parse.ts b/eslint/src/rules/4/deprecated.media-rule-list-parse.ts index cc4ba6ac0..2dc15e368 100644 --- a/eslint/src/rules/4/deprecated.media-rule-list-parse.ts +++ b/eslint/src/rules/4/deprecated.media-rule-list-parse.ts @@ -8,12 +8,11 @@ import type {ESLintReplacementMethodCfg} from '../../core/deprecated-class-metho const AVAILABLE_SINCE = '5.0.0-beta.24'; const isActual = lte(ESL_PACKAGE_VERSION, AVAILABLE_SINCE); -const isTest = process.env.NODE_ENV === 'test'; /** * Rule for deprecated 'parse' method of {@link ESLMediaRuleList} */ -export default isActual || isTest ? +export default isActual ? buildRule({ className: 'ESLMediaRuleList', deprecatedMethod: 'parse',