Skip to content

Commit 20cd1fd

Browse files
authored
Merge pull request #182 from declandewet/nested-html-fix
Fix the case of nested code for the source tag
2 parents fc44284 + d5f710f commit 20cd1fd

18 files changed

+326
-16
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<body>
4+
<ul>
5+
6+
</ul>
7+
</body>
8+
</html>

src/html/fixtures/empty-array.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<body>
4+
<ul></ul>
5+
</body>
6+
</html>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<body>
4+
<ul><li>
5+
<div>apple</div>
6+
</li> <li>
7+
<div>banana</div>
8+
</li> <li>
9+
<div>kiwi</div>
10+
</li></ul>
11+
</body>
12+
</html>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<body>
4+
<ul>
5+
6+
<li>
7+
<div>apple</div>
8+
</li>
9+
<li>
10+
<div>banana</div>
11+
</li>
12+
<li>
13+
<div>kiwi</div>
14+
</li>
15+
16+
</ul>
17+
</body>
18+
</html>

src/html/fixtures/nesting.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<body>
4+
<ul>
5+
<li>
6+
<div>apple</div>
7+
</li>
8+
<li>
9+
<div>banana</div>
10+
</li>
11+
<li>
12+
<div>kiwi</div>
13+
</li>
14+
</ul>
15+
</body>
16+
</html>

src/html/html.test.js

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,111 @@ test('does not introduce excess newlines', () => {
4545
`;
4646
expect(actual).toBe(expected);
4747
});
48+
49+
test('renders nested HTML', () => {
50+
const fruits = ['apple', 'banana', 'kiwi'];
51+
const expected = readFromFixture(__dirname, 'nesting');
52+
53+
function renderFruit(fruit) {
54+
return html`
55+
<li>
56+
<div>${fruit}</div>
57+
</li>
58+
`;
59+
}
60+
61+
const actual = html`
62+
<!DOCTYPE html>
63+
<html lang="en">
64+
<body>
65+
<ul>
66+
${fruits.map(renderFruit)}
67+
</ul>
68+
</body>
69+
</html>
70+
`;
71+
72+
expect(actual).toBe(expected);
73+
});
74+
75+
test('renders nested HTML without excess empty lines', () => {
76+
const fruits = ['apple', 'banana', 'kiwi'];
77+
const expected = readFromFixture(__dirname, 'nesting-no-excess');
78+
79+
function renderFruit(fruit) {
80+
return html`
81+
<li>
82+
<div>${fruit}</div>
83+
</li>
84+
`;
85+
}
86+
87+
const actual = html`
88+
<!DOCTYPE html>
89+
<html lang="en">
90+
<body>
91+
<ul>
92+
93+
${fruits.map(renderFruit)}
94+
95+
</ul>
96+
</body>
97+
</html>
98+
`;
99+
100+
expect(actual).toBe(expected);
101+
});
102+
103+
test("just strips indent when there's an empty array inside", () => {
104+
const expected = readFromFixture(__dirname, 'empty-array');
105+
const actual = html`
106+
<!DOCTYPE html>
107+
<html lang="en">
108+
<body>
109+
<ul>${[]}</ul>
110+
</body>
111+
</html>
112+
`;
113+
114+
expect(actual).toBe(expected);
115+
});
116+
117+
test("just strips indent when there's an empty array inside (multiline)", () => {
118+
const expected = readFromFixture(__dirname, 'empty-array-multiline');
119+
const actual = html`
120+
<!DOCTYPE html>
121+
<html lang="en">
122+
<body>
123+
<ul>
124+
${[]}
125+
</ul>
126+
</body>
127+
</html>
128+
`;
129+
130+
expect(actual).toBe(expected);
131+
});
132+
133+
test('may not indent as expected when the array is not in a new line', () => {
134+
const fruits = ['apple', 'banana', 'kiwi'];
135+
const expected = readFromFixture(__dirname, 'nesting-improper');
136+
137+
function renderFruit(fruit) {
138+
return html`
139+
<li>
140+
<div>${fruit}</div>
141+
</li>
142+
`;
143+
}
144+
145+
const actual = html`
146+
<!DOCTYPE html>
147+
<html lang="en">
148+
<body>
149+
<ul>${fruits.map(renderFruit)}</ul>
150+
</body>
151+
</html>
152+
`;
153+
154+
expect(actual).toBe(expected);
155+
});

src/inlineArrayTransformer/inlineArrayTransformer.js

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import { prefixLines, stripLastNewLine } from '../utils';
2+
13
/**
24
* Converts an array substitution to a string containing a list
3-
* @param {String} [opts.separator = ''] - the character that separates each item
4-
* @param {String} [opts.conjunction = ''] - replace the last separator with this
5-
* @param {Boolean} [opts.serial = false] - include the separator before the conjunction? (Oxford comma use-case)
5+
* @param {String} [opts.separator = ''] - The character that separates each item
6+
* @param {String} [opts.conjunction = ''] - Replace the last separator with this
7+
* @param {Boolean} [opts.serial = false] - Include the separator before the conjunction? (Oxford comma use-case)
68
*
7-
* @return {Object} - a TemplateTag transformer
9+
* @return {Object} - A transformer
810
*/
911
const inlineArrayTransformer = ({
1012
conjunction = '',
@@ -17,20 +19,30 @@ const inlineArrayTransformer = ({
1719
return substitution;
1820
}
1921

20-
// be sure to maintain indentation
21-
const indent = resultSoFar.match(/(\n?[^\S\n]+)$/);
22-
const fullSeparator = separator.concat(indent ? indent[1] : ' ');
23-
const fullConjunction = ''.concat(conjunction, ' ');
24-
const conjunctionIndex = conjunction ? substitution.length - 1 : -1;
22+
const { length } = substitution;
23+
const lastSeparatorIndex = conjunction && !serial ? length - 2 : length - 1;
24+
const indentation = resultSoFar.match(/(?:\n)([^\S\n]+)$/);
25+
26+
if (conjunction && length > 1) {
27+
substitution[length - 1] = ''.concat(
28+
conjunction,
29+
' ',
30+
substitution[length - 1],
31+
);
32+
}
2533

26-
return substitution.reduce((result, part, index) =>
27-
''.concat(
34+
return substitution.reduce((result, part, index) => {
35+
const isFirstPart = index === 0;
36+
const strippedPart = stripLastNewLine(part);
37+
return ''.concat(
2838
result,
29-
index !== conjunctionIndex || serial ? fullSeparator : ' ',
30-
index === conjunctionIndex ? fullConjunction : '',
31-
part,
32-
),
33-
);
39+
isFirstPart ? '' : indentation ? '\n' : ' ',
40+
indentation
41+
? prefixLines(indentation[1], strippedPart, isFirstPart)
42+
: strippedPart,
43+
index < lastSeparatorIndex ? separator : '',
44+
);
45+
}, '');
3446
},
3547
});
3648

src/inlineArrayTransformer/inlineArrayTransformer.test.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,38 @@ test('maintains indentation', () => {
6262
);
6363
});
6464

65+
test('maintains indentation in multiline strings', () => {
66+
const tag = createTag(inlineArrayTransformer({ separator: ',' }));
67+
expect(tag`My friends are always
68+
${['dra-\nmatic', 'emo-\ntional', 'nee-\ndy']}`).toBe(
69+
'My friends are always\n dra-\n matic,\n emo-\n tional,\n nee-\n dy',
70+
);
71+
});
72+
73+
test('maintains indentation in multiline strings (with conjunction)', () => {
74+
const tag = createTag(
75+
inlineArrayTransformer({ separator: ',', conjunction: 'and' }),
76+
);
77+
expect(tag`My friends are always
78+
${['dra-\nmatic', 'emo-\ntional', 'nee-\ndy']}`).toBe(
79+
'My friends are always\n dra-\n matic,\n emo-\n tional\n and nee-\n dy',
80+
);
81+
});
82+
83+
test('maintains indentation in multiline strings (with serial/oxford separators)', () => {
84+
const tag = createTag(
85+
inlineArrayTransformer({
86+
separator: ',',
87+
conjunction: 'and',
88+
serial: true,
89+
}),
90+
);
91+
expect(tag`My friends are always
92+
${['dra-\nmatic', 'emo-\ntional', 'nee-\ndy']}`).toBe(
93+
'My friends are always\n dra-\n matic,\n emo-\n tional,\n and nee-\n dy',
94+
);
95+
});
96+
6597
test('does not introduce excess newlines', () => {
6698
const tag = createTag(inlineArrayTransformer());
6799
expect(tag`My friends are always

src/utils/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
11
export flat from './flat';
2+
export prefixLines from './prefixLines';
3+
export stripLastNewLine from './stripLastNewLine';
4+
export toString from './toString';

src/utils/prefixLines/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default from './prefixLines';

0 commit comments

Comments
 (0)