Skip to content

Commit 74437b0

Browse files
Inline styles per line
1 parent 7a81515 commit 74437b0

10 files changed

+357
-31
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "vue3-snapshot-serializer",
33
"type": "module",
4-
"version": "2.9.0",
4+
"version": "2.10.0",
55
"description": "Vitest snapshot serializer for Vue 3 components",
66
"main": "index.js",
77
"scripts": {

src/formatters/diffable.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
import {
2020
debugLogger,
2121
escapeHtml,
22+
parseInlineStyles,
2223
parseMarkup,
2324
unescapeHtml
2425
} from '../helpers.js';
@@ -209,6 +210,27 @@ export const diffableFormatter = function (markup) {
209210
}
210211
}
211212
}
213+
214+
if (attribute.name === 'style') {
215+
const styles = parseInlineStyles(attributeValue);
216+
const stylesOnNewLine = styles.length > options.inlineStylesPerLine;
217+
if (stylesOnNewLine) {
218+
const multiLineStyles = styles
219+
.map((inlineStyle) => {
220+
if (isNewLine) {
221+
return '\n' + ' '.repeat(indent + 2) + inlineStyle;
222+
}
223+
return '\n' + ' '.repeat(indent + 1) + inlineStyle;
224+
})
225+
.join('');
226+
if (isNewLine) {
227+
attributeValue = multiLineStyles + '\n' + ' '.repeat(indent + 1);
228+
} else {
229+
attributeValue = multiLineStyles + '\n' + ' '.repeat(indent);
230+
}
231+
}
232+
}
233+
212234
fullAttribute = attribute.name + '="' + attributeValue + '"';
213235
} else {
214236
fullAttribute = attribute.name;

src/helpers.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,66 @@ export const parseMarkup = function (markup) {
249249
return ast;
250250
};
251251

252+
/**
253+
* Takes in the value from an HTML style attribute.
254+
* Splits on the semi-colon, handling edge cases.
255+
*
256+
* @example
257+
* const input = ' color:#F00; padding: 2px ';
258+
* const output = parseInlineStyles(input);
259+
* expect(output)
260+
* .toEqual(['color:#F00;', 'padding: 2px;']);
261+
*
262+
* @param {string} styles Any string of inline styles
263+
* @return {string[]} Array of separated inline styles
264+
*/
265+
export const parseInlineStyles = function (styles) {
266+
debugLogger({ function: 'helpers.js:parseInlineStyles' });
267+
if (!styles) {
268+
return [];
269+
}
270+
271+
const pairs = [];
272+
let current = '';
273+
let insideSingleQuote = false;
274+
let insideDoubleQuote = false;
275+
let parenthesisCount = 0;
276+
277+
for (let i = 0; i < styles.length; i++) {
278+
const character = styles[i];
279+
const previousCharacter = styles[i - 1];
280+
const isEscaped = previousCharacter === '\\';
281+
282+
if (character === '\'' && !insideDoubleQuote && !isEscaped) {
283+
insideSingleQuote = !insideSingleQuote;
284+
} else if (character === '"' && !insideSingleQuote && !isEscaped) {
285+
insideDoubleQuote = !insideDoubleQuote;
286+
} else if (character === '(' && !insideSingleQuote && !insideDoubleQuote) {
287+
parenthesisCount = parenthesisCount + 1;
288+
} else if (character === ')' && !insideSingleQuote && !insideDoubleQuote && parenthesisCount) {
289+
parenthesisCount = parenthesisCount - 1;
290+
}
291+
292+
if (character === ';' && !insideSingleQuote && !insideDoubleQuote && !parenthesisCount) {
293+
pairs.push(current);
294+
current = '';
295+
} else {
296+
current = current + character;
297+
}
298+
}
299+
300+
// Add the last pair
301+
pairs.push(current);
302+
303+
return pairs
304+
.map((pair) => {
305+
return pair.trim() + ';';
306+
})
307+
.filter((pair) => {
308+
return pair && pair !== ';';
309+
});
310+
};
311+
252312
/**
253313
* Creates a cheerio ($) object from the html for DOM manipulation.
254314
*

src/loadOptions.js

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -301,35 +301,34 @@ export const loadOptions = function () {
301301
globalThis.vueSnapshots.formatting.tagsWithWhitespacePreserved = TAGS_WITH_WHITESPACE_PRESERVED_DEFAULTS;
302302
}
303303

304-
// Formatting - Attributes Per Line
305-
if (
306-
typeof(globalThis.vueSnapshots.formatting.attributesPerLine) !== 'number' ||
307-
globalThis.vueSnapshots.formatting.attributesPerLine < 0 ||
308-
globalThis.vueSnapshots.formatting.attributesPerLine % 1 !== 0
309-
) {
310-
if (globalThis.vueSnapshots.formatting.attributesPerLine !== undefined) {
311-
logger([
312-
'global.vueSnapshots.formatting.attributesPerLine',
313-
'must be a whole number.'
314-
].join(' '));
304+
/**
305+
* The validation for attributesPerLine, classesPerLine, and inlineStylesPerLine
306+
* are all identical, so this DRYs them up.
307+
*
308+
* @param {string} setting The setting name, without "PerLine"
309+
*/
310+
function perLine (setting) {
311+
if (
312+
typeof(globalThis.vueSnapshots.formatting[setting + 'PerLine']) !== 'number' ||
313+
globalThis.vueSnapshots.formatting[setting + 'PerLine'] < 0 ||
314+
globalThis.vueSnapshots.formatting[setting + 'PerLine'] % 1 !== 0
315+
) {
316+
if (globalThis.vueSnapshots.formatting[setting + 'PerLine'] !== undefined) {
317+
logger([
318+
'global.vueSnapshots.formatting.' + setting + 'PerLine',
319+
'must be a whole number.'
320+
].join(' '));
321+
}
322+
globalThis.vueSnapshots.formatting[setting + 'PerLine'] = 1;
315323
}
316-
globalThis.vueSnapshots.formatting.attributesPerLine = 1;
317324
}
318325

326+
// Formatting - Attributes Per Line
327+
perLine('attributes');
319328
// Formatting - Classes Per Line
320-
if (
321-
typeof(globalThis.vueSnapshots.formatting.classesPerLine) !== 'number' ||
322-
globalThis.vueSnapshots.formatting.classesPerLine < 0 ||
323-
globalThis.vueSnapshots.formatting.classesPerLine % 1 !== 0
324-
) {
325-
if (globalThis.vueSnapshots.formatting.classesPerLine !== undefined) {
326-
logger([
327-
'global.vueSnapshots.formatting.classesPerLine',
328-
'must be a whole number.'
329-
].join(' '));
330-
}
331-
globalThis.vueSnapshots.formatting.classesPerLine = 1;
332-
}
329+
perLine('classes');
330+
// Formatting - Inline Styles Per Line
331+
perLine('inlineStyles');
333332

334333
// Formatting - Void Elements
335334
if (!ALLOWED_VOID_ELEMENTS.includes(globalThis.vueSnapshots.formatting.voidElements)) {
@@ -398,6 +397,7 @@ export const loadOptions = function () {
398397
...Object.keys(formattingBooleanDefaults),
399398
'attributesPerLine',
400399
'classesPerLine',
400+
'inlineStylesPerLine',
401401
'tagsWithWhitespacePreserved',
402402
'voidElements',
403403
'whiteSpacePreservedOption'

tests/unit/src/cheerioManipulation.test.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,10 @@ describe('Cheerio Manipulation', () => {
418418
.toMatchInlineSnapshot(`
419419
<h1
420420
class="active"
421-
style="background: #F00; width: 0px;"
421+
style="
422+
background: #F00;
423+
width: 0px;
424+
"
422425
title="{ a: 2 }"
423426
>
424427
Text
@@ -436,7 +439,10 @@ describe('Cheerio Manipulation', () => {
436439
.toMatchInlineSnapshot(`
437440
<h1
438441
class="active"
439-
style="background: #F00; width: 0px;"
442+
style="
443+
background: #F00;
444+
width: 0px;
445+
"
440446
title="[object Object]"
441447
>
442448
Text

0 commit comments

Comments
 (0)