From 0321073b969f00838570f6b74e5d6c12410783e5 Mon Sep 17 00:00:00 2001 From: Matthew Wang Date: Tue, 11 Apr 2023 18:35:18 -0700 Subject: [PATCH 1/5] First draft with custom resolvers --- .changeset/many-bikes-rescue.md | 5 ++ .../__tests__/index.js | 5 ++ .../index.js | 66 +++++++++++++++---- 3 files changed, 65 insertions(+), 11 deletions(-) create mode 100644 .changeset/many-bikes-rescue.md diff --git a/.changeset/many-bikes-rescue.md b/.changeset/many-bikes-rescue.md new file mode 100644 index 0000000000..f95fb15428 --- /dev/null +++ b/.changeset/many-bikes-rescue.md @@ -0,0 +1,5 @@ +--- +"stylelint": patch +--- + +Fixed: `declaration-block-no-redundant-longhand-properties` autofix for `grid-template` diff --git a/lib/rules/declaration-block-no-redundant-longhand-properties/__tests__/index.js b/lib/rules/declaration-block-no-redundant-longhand-properties/__tests__/index.js index c8f42136d2..9814d3dad2 100644 --- a/lib/rules/declaration-block-no-redundant-longhand-properties/__tests__/index.js +++ b/lib/rules/declaration-block-no-redundant-longhand-properties/__tests__/index.js @@ -144,6 +144,11 @@ testRule({ fixed: 'a { inset: 0 0 0 0; }', message: messages.expected('inset'), }, + { + code: 'a { grid-template-rows: var(--header-h) 1fr var(--footer-h); grid-template-columns: var(--toolbar-w) 1fr; grid-template-areas: "header header" "toolbar main" "footer footer"; }', + fixed: `a { grid-template: "header header" var(--header-h) "toolbar main" 1fr "footer footer" var(--footer-h) / var(--toolbar-w) 1fr; }`, + message: messages.expected('grid-template'), + }, ], }); diff --git a/lib/rules/declaration-block-no-redundant-longhand-properties/index.js b/lib/rules/declaration-block-no-redundant-longhand-properties/index.js index 0c254f4163..aa63226de4 100644 --- a/lib/rules/declaration-block-no-redundant-longhand-properties/index.js +++ b/lib/rules/declaration-block-no-redundant-longhand-properties/index.js @@ -22,8 +22,49 @@ const meta = { fixable: true, }; +const customResolvers = new Map([ + [ + 'grid-template', + (/** @type {Map} */ transformedDeclarationNodes) => { + const areas = transformedDeclarationNodes.get('grid-template-areas')?.value.trim(); + const columns = transformedDeclarationNodes.get('grid-template-columns')?.value.trim(); + const rows = transformedDeclarationNodes.get('grid-template-rows')?.value.trim(); + + if (areas === undefined || columns === undefined || rows === undefined) return; + + const splitAreas = [...areas.matchAll(/".+?"/g)].map((x) => x[0]); + const splitRows = rows.split(' '); + + if (splitAreas.length !== splitRows.length) return; + + const zipped = splitAreas.map((area, i) => `${area} ${splitRows[i]}`).join(' '); + + return `${zipped} / ${columns}`; + }, + ], +]); + /** @typedef {import('postcss').Declaration} Declaration */ +const resolveShorthandValue = ( + /** @type {string} */ prefixedShorthandProperty, + /** @type {string[]} */ prefixedShorthandData, + /** @type {Map} */ transformedDeclarationNodes, +) => { + const resolver = customResolvers.get(prefixedShorthandProperty); + + if (resolver === undefined) { + // the "default" resolver: sort the longhand values in the order + // of their properties + return prefixedShorthandData + .map((p) => transformedDeclarationNodes.get(p)?.value.trim()) + .filter(Boolean) + .join(' '); + } + + return resolver(transformedDeclarationNodes) ?? null; +}; + /** @type {import('stylelint').Rule} */ const rule = (primary, secondaryOptions, context) => { return (root, result) => { @@ -113,21 +154,24 @@ const rule = (primary, secondaryOptions, context) => { const transformedDeclarationNodes = new Map( declNodes.map((d) => [d.prop.toLowerCase(), d]), ); - const resolvedShorthandValue = prefixedShorthandData - .map((p) => transformedDeclarationNodes.get(p)?.value.trim()) - .filter(Boolean) - .join(' '); + const resolvedShorthandValue = resolveShorthandValue( + prefixedShorthandProperty, + prefixedShorthandData, + transformedDeclarationNodes, + ); - const newShorthandDeclarationNode = firstDeclNode.clone({ - prop: prefixedShorthandProperty, - value: resolvedShorthandValue, - }); + if (resolvedShorthandValue !== null) { + const newShorthandDeclarationNode = firstDeclNode.clone({ + prop: prefixedShorthandProperty, + value: resolvedShorthandValue, + }); - firstDeclNode.replaceWith(newShorthandDeclarationNode); + firstDeclNode.replaceWith(newShorthandDeclarationNode); - declNodes.forEach((node) => node.remove()); + declNodes.forEach((node) => node.remove()); - return; + return; + } } } From bdffe3d29a5cf6b971d22579b7ac14354cfa14f1 Mon Sep 17 00:00:00 2001 From: Matt Wang Date: Thu, 20 Apr 2023 12:11:48 -0700 Subject: [PATCH 2/5] Apply suggestions from code review Co-authored-by: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> --- .../index.js | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/lib/rules/declaration-block-no-redundant-longhand-properties/index.js b/lib/rules/declaration-block-no-redundant-longhand-properties/index.js index aa63226de4..3885b23559 100644 --- a/lib/rules/declaration-block-no-redundant-longhand-properties/index.js +++ b/lib/rules/declaration-block-no-redundant-longhand-properties/index.js @@ -22,19 +22,22 @@ const meta = { fixable: true, }; +/** @type {Map) => (string | undefined)>} */ const customResolvers = new Map([ [ 'grid-template', - (/** @type {Map} */ transformedDeclarationNodes) => { - const areas = transformedDeclarationNodes.get('grid-template-areas')?.value.trim(); - const columns = transformedDeclarationNodes.get('grid-template-columns')?.value.trim(); - const rows = transformedDeclarationNodes.get('grid-template-rows')?.value.trim(); + (decls) => { + const areas = decls.get('grid-template-areas')?.value.trim(); + const columns = decls.get('grid-template-columns')?.value.trim(); + const rows = decls.get('grid-template-rows')?.value.trim(); - if (areas === undefined || columns === undefined || rows === undefined) return; + if (!(areas && columns && rows)) return; - const splitAreas = [...areas.matchAll(/".+?"/g)].map((x) => x[0]); + const splitAreas = [...areas.matchAll(/"[^"]+?"/g)].map((x) => x[0]); const splitRows = rows.split(' '); + if (splitAreas.length === 0 || splitRows.length === 0) return; + if (splitAreas.length !== splitRows.length) return; const zipped = splitAreas.map((area, i) => `${area} ${splitRows[i]}`).join(' '); @@ -46,23 +49,30 @@ const customResolvers = new Map([ /** @typedef {import('postcss').Declaration} Declaration */ +/** + * @param {string} prefixedShorthandProperty + * @param {string[]} prefixedShorthandData + * @param {Map} transformedDeclarationNodes + * @returns {string | undefined} + */ const resolveShorthandValue = ( - /** @type {string} */ prefixedShorthandProperty, - /** @type {string[]} */ prefixedShorthandData, - /** @type {Map} */ transformedDeclarationNodes, + prefixedShorthandProperty, + prefixedShorthandData, + transformedDeclarationNodes, ) => { const resolver = customResolvers.get(prefixedShorthandProperty); if (resolver === undefined) { // the "default" resolver: sort the longhand values in the order // of their properties - return prefixedShorthandData + const values = prefixedShorthandData .map((p) => transformedDeclarationNodes.get(p)?.value.trim()) - .filter(Boolean) - .join(' '); + .filter(Boolean); + + return values.length > 0 ? values.join(' ') : undefined; } - return resolver(transformedDeclarationNodes) ?? null; + return resolver(transformedDeclarationNodes); }; /** @type {import('stylelint').Rule} */ From 32f15f608398d3a2d86ecccdc7082e677ab23d64 Mon Sep 17 00:00:00 2001 From: Matt Wang Date: Thu, 20 Apr 2023 12:12:00 -0700 Subject: [PATCH 3/5] Update lib/rules/declaration-block-no-redundant-longhand-properties/index.js Co-authored-by: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> --- .../declaration-block-no-redundant-longhand-properties/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/declaration-block-no-redundant-longhand-properties/index.js b/lib/rules/declaration-block-no-redundant-longhand-properties/index.js index 3885b23559..7fd3964293 100644 --- a/lib/rules/declaration-block-no-redundant-longhand-properties/index.js +++ b/lib/rules/declaration-block-no-redundant-longhand-properties/index.js @@ -170,7 +170,7 @@ const rule = (primary, secondaryOptions, context) => { transformedDeclarationNodes, ); - if (resolvedShorthandValue !== null) { + if (resolvedShorthandValue) { const newShorthandDeclarationNode = firstDeclNode.clone({ prop: prefixedShorthandProperty, value: resolvedShorthandValue, From 8040347f10dc2d62755471c026931b3cc12aeda0 Mon Sep 17 00:00:00 2001 From: Matthew Wang Date: Thu, 20 Apr 2023 12:13:54 -0700 Subject: [PATCH 4/5] Touch up code suggestions --- .../index.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/rules/declaration-block-no-redundant-longhand-properties/index.js b/lib/rules/declaration-block-no-redundant-longhand-properties/index.js index 7fd3964293..25937abf6f 100644 --- a/lib/rules/declaration-block-no-redundant-longhand-properties/index.js +++ b/lib/rules/declaration-block-no-redundant-longhand-properties/index.js @@ -22,6 +22,8 @@ const meta = { fixable: true, }; +/** @typedef {import('postcss').Declaration} Declaration */ + /** @type {Map) => (string | undefined)>} */ const customResolvers = new Map([ [ @@ -33,13 +35,11 @@ const customResolvers = new Map([ if (!(areas && columns && rows)) return; - const splitAreas = [...areas.matchAll(/"[^"]+?"/g)].map((x) => x[0]); + const splitAreas = [...areas.matchAll(/"[^"]+"/g)].map((x) => x[0]); const splitRows = rows.split(' '); if (splitAreas.length === 0 || splitRows.length === 0) return; - if (splitAreas.length !== splitRows.length) return; - const zipped = splitAreas.map((area, i) => `${area} ${splitRows[i]}`).join(' '); return `${zipped} / ${columns}`; @@ -47,8 +47,6 @@ const customResolvers = new Map([ ], ]); -/** @typedef {import('postcss').Declaration} Declaration */ - /** * @param {string} prefixedShorthandProperty * @param {string[]} prefixedShorthandData @@ -69,7 +67,7 @@ const resolveShorthandValue = ( .map((p) => transformedDeclarationNodes.get(p)?.value.trim()) .filter(Boolean); - return values.length > 0 ? values.join(' ') : undefined; + return values.length > 0 ? values.join(' ') : undefined; } return resolver(transformedDeclarationNodes); From b0c82762804949049c79e14bba8006a10178c02f Mon Sep 17 00:00:00 2001 From: Matthew Wang Date: Thu, 20 Apr 2023 12:15:04 -0700 Subject: [PATCH 5/5] Fix incorrectly-applied suggestion --- .../declaration-block-no-redundant-longhand-properties/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/rules/declaration-block-no-redundant-longhand-properties/index.js b/lib/rules/declaration-block-no-redundant-longhand-properties/index.js index 25937abf6f..7121315715 100644 --- a/lib/rules/declaration-block-no-redundant-longhand-properties/index.js +++ b/lib/rules/declaration-block-no-redundant-longhand-properties/index.js @@ -40,6 +40,8 @@ const customResolvers = new Map([ if (splitAreas.length === 0 || splitRows.length === 0) return; + if (splitAreas.length !== splitRows.length) return; + const zipped = splitAreas.map((area, i) => `${area} ${splitRows[i]}`).join(' '); return `${zipped} / ${columns}`;