-
Notifications
You must be signed in to change notification settings - Fork 439
Expand file tree
/
Copy pathswap-lwc-style-for-style.ts
More file actions
105 lines (98 loc) · 4.64 KB
/
swap-lwc-style-for-style.ts
File metadata and controls
105 lines (98 loc) · 4.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
/**
* SSRv2 implements style deduplication, where an <lwc-style> tag is inserted alongside the first instance
* of a given component's <style> tag. In subsequent renders of that component, only the <lwc-style> tag
* is present in the generated markup.
*
* This presents a problem when sharing fixtures between SSRv1 and SSRv2. SSRv1 will expect to find repeated
* <style> tags and won't expect to find <lwc-style> tags.
*
* The `swapLwcStyleForStyleTag` function takes the SSRv2 fixtures and modifies it to look how SSRv1 would
* expect. As more divergences between the implementations crop up, we may need to add more fixture mutations
* to accommodate the differences.
*/
interface StyleTagInfo {
originalStyleTag: string;
styleTagWithoutId: string;
idAttrStr: string;
styleId: string;
styleTagIdx: number;
indentation: number;
}
export function swapLwcStyleForStyleTag(src: string): string {
let modifiedSrc = src;
// ([leading whitespace])(<style (id="(text)")...>...</style>)
const styleCapture = /(\s*)(<style [^>]*(id="([^"]+)" ?)[^>]*>.*?<\/style>)/s;
const capturedStyleTags: StyleTagInfo[] = [];
let styleTagMatch: RegExpExecArray | null;
// Find all <style> tags with an id, remove id attrs, capture information for later
// replacement of <lwc-style> tags. The length of the `modifiedSrc` string will be
// changing as <style> tags are modified, so we need to exec each time.
while ((styleTagMatch = styleCapture.exec(modifiedSrc))) {
const [tagWithLeadingWhitespace, leadingWhiteSpace, originalStyleTag, idAttrStr, styleId] =
styleTagMatch;
const styleTagWithoutId = originalStyleTag.replace(idAttrStr, '');
const styleTagIdx = styleTagMatch.index;
// We remove the value of `styleTagWithoutId` here because it'll be added back in when the first
// corresponding <lwc-style> tag is replaced with <style> in the second section below.
modifiedSrc =
modifiedSrc.slice(0, styleTagIdx) +
modifiedSrc.slice(styleTagIdx + tagWithLeadingWhitespace.length);
capturedStyleTags.push({
originalStyleTag,
styleTagWithoutId,
idAttrStr,
styleId,
styleTagIdx,
indentation: leadingWhiteSpace.length,
});
}
const lwcStyleCapture =
/(\s*)<lwc-style(?: class="[^"]*")? style-id="([^"]+)">[^<]*<\/lwc-style>/s;
const idToStyleTag = Object.fromEntries(
capturedStyleTags.map((styleTagInfo) => [styleTagInfo.styleId, styleTagInfo])
);
let lwcStyleTagMatch: RegExpExecArray | null;
// Find all <lwc-style> tags and replace them with corresponding <style> tags captured earlier.
// The length of the `modifiedSrc` string will be changing as <style> tags are modified, so we
// need to exec each time.
while ((lwcStyleTagMatch = lwcStyleCapture.exec(modifiedSrc))) {
const [tagWithLeadingWhitespace, leadingWhiteSpace, styleId] = lwcStyleTagMatch;
const lwcStyleTagIdx = lwcStyleTagMatch.index;
const correspondingStyleTagInfo = idToStyleTag[styleId];
if (!correspondingStyleTagInfo) {
throw new Error(
'swapLwcStyleForStyleTag was unable to find <style> tag corresponding to <lwc-style>'
);
}
let styleTagWithoutId = correspondingStyleTagInfo.styleTagWithoutId;
const indentationDelta = correspondingStyleTagInfo.indentation
? leadingWhiteSpace.length - correspondingStyleTagInfo.indentation
: null;
if (indentationDelta) {
styleTagWithoutId = styleTagWithoutId
.split('\n')
.map((line, idx) => {
if (
// Don't add indentation for lines that are empty.
!line ||
// The first line will already be indented.
idx === 0
) {
return line;
}
// The original <style> is more indented than the <lwc-style>
if (indentationDelta > 0) {
return line.padStart(line.length + indentationDelta);
}
// The original <style> is less indented than the <lwc-style>
return line.slice(-indentationDelta);
})
.join('\n');
}
modifiedSrc =
modifiedSrc.slice(0, lwcStyleTagIdx + leadingWhiteSpace.length) +
styleTagWithoutId +
modifiedSrc.slice(lwcStyleTagIdx + tagWithLeadingWhitespace.length);
}
return modifiedSrc;
}