Skip to content

Commit 14039d8

Browse files
authored
Merge pull request #155 from styled-components/minify-single-line-comment
fix single line comments + interpolation issues
2 parents e04eb6f + 9ed3e72 commit 14039d8

File tree

8 files changed

+175
-31
lines changed

8 files changed

+175
-31
lines changed

src/css/placeholderUtils.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
// The capture group makes sure that the split contains the interpolation index
2-
const placeholderRegex = /__PLACEHOLDER_(\d+?)__/
2+
export const placeholderRegex = /(?:__PLACEHOLDER_(\d+)__)/g
33

44
// Alternative regex that splits without a capture group
5-
const placeholderNonCapturingRegex = /__PLACEHOLDER_(?:\d+?)__/
5+
const placeholderNonCapturingRegex = /__PLACEHOLDER_(?:\d+)__/g
66

77
// Generates a placeholder from an index
88
export const makePlaceholder = index => `__PLACEHOLDER_${index}__`
99

1010
// Splits CSS by placeholders
11-
export const splitByPlaceholders = (css, capture = true) =>
12-
css.split(capture ? placeholderRegex : placeholderNonCapturingRegex)
11+
export const splitByPlaceholders = ([css, ...rest], capture = true) => [
12+
css.split(capture ? placeholderRegex : placeholderNonCapturingRegex),
13+
...rest,
14+
]

src/minify/index.js

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,18 @@
1-
import { makePlaceholder, splitByPlaceholders } from '../css/placeholderUtils'
1+
import difference from 'lodash/difference'
2+
3+
import {
4+
makePlaceholder,
5+
placeholderRegex,
6+
splitByPlaceholders,
7+
} from '../css/placeholderUtils'
8+
9+
const injectUniquePlaceholders = strArr => {
10+
let i = 0
11+
12+
return strArr.reduce((str, val, index, arr) => {
13+
return str + val + (index < arr.length - 1 ? makePlaceholder(i++) : '')
14+
}, '')
15+
}
216

317
const makeMultilineCommentRegex = newlinePattern =>
418
new RegExp('\\/\\*[^!](.|' + newlinePattern + ')*?\\*\\/', 'g')
@@ -75,18 +89,23 @@ const minify = linebreakPattern => {
7589
.map(stripLineComment) // Remove line comments inside text
7690
.join(' ') // Rejoin all lines
7791

78-
return compressSymbols(newCode)
92+
const eliminatedExpressionIndices = difference(
93+
code.match(placeholderRegex),
94+
newCode.match(placeholderRegex)
95+
).map(x => parseInt(x.match(/\d+/)[0], 10))
96+
97+
return [compressSymbols(newCode), eliminatedExpressionIndices]
7998
}
8099
}
81100

82101
export const minifyRaw = minify('(?:\\\\r|\\\\n|\\r|\\n)')
83102
export const minifyCooked = minify('[\\r\\n]')
84103

85104
export const minifyRawValues = rawValues =>
86-
splitByPlaceholders(minifyRaw(rawValues.join(makePlaceholder(123))), false)
105+
splitByPlaceholders(minifyRaw(injectUniquePlaceholders(rawValues)), false)
87106

88107
export const minifyCookedValues = cookedValues =>
89108
splitByPlaceholders(
90-
minifyCooked(cookedValues.join(makePlaceholder(123))),
109+
minifyCooked(injectUniquePlaceholders(cookedValues)),
91110
false
92111
)

src/visitors/minify.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,18 @@ export default t => (path, state) => {
1010
const templateLiteral = path.node.quasi
1111
const quasisLength = templateLiteral.quasis.length
1212

13-
const rawValuesMinified = minifyRawValues(
13+
const [rawValuesMinified] = minifyRawValues(
1414
templateLiteral.quasis.map(x => x.value.raw)
1515
)
16-
const cookedValuesMinfified = minifyCookedValues(
17-
templateLiteral.quasis.map(x => x.value.cooked)
18-
)
16+
17+
const [
18+
cookedValuesMinfified,
19+
eliminatedExpressionIndices,
20+
] = minifyCookedValues(templateLiteral.quasis.map(x => x.value.cooked))
21+
22+
eliminatedExpressionIndices.forEach((expressionIndex, iteration) => {
23+
templateLiteral.expressions.splice(expressionIndex - iteration, 1)
24+
})
1925

2026
for (let i = 0; i < quasisLength; i++) {
2127
const element = templateLiteral.quasis[i]

src/visitors/templateLiterals/transpile.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ export default t => (path, state) => {
88
} = path.node
99

1010
const values = t.arrayExpression(
11-
quasis.map(quasi => t.stringLiteral(quasi.value.cooked))
11+
quasis
12+
.filter(quasi => quasi.value.cooked !== undefined)
13+
.map(quasi => t.stringLiteral(quasi.value.cooked))
1214
)
1315

1416
path.replaceWith(t.callExpression(callee, [values, ...expressions]))

test/__snapshots__/index.test.js.snap

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,45 @@ const Parens = styled(\'div\')\`&:hover{color:blue;}color:red;\`;
158158
const UrlComments = styled(\'div\')\`color:red;background:red;border:1px solid green;\`;"
159159
`;
160160
161+
exports[`fixtures should minify single line comments with interpolations 1`] = `
162+
"import styled from \'styled-components/no-tags\';
163+
164+
const Test1 = styled(\'div\').withConfig({
165+
displayName: \'minify-single-line-comments-with-interpolations__Test1\',
166+
componentId: \'sc-1p41dy5-0\'
167+
})([\'width:100%;\']);
168+
169+
const Test2 = styled(\'div\').withConfig({
170+
displayName: \'minify-single-line-comments-with-interpolations__Test2\',
171+
componentId: \'sc-1p41dy5-1\'
172+
})([\'width:100%;\']);
173+
174+
const Test3 = styled(\'div\').withConfig({
175+
displayName: \'minify-single-line-comments-with-interpolations__Test3\',
176+
componentId: \'sc-1p41dy5-2\'
177+
})([\'width:100%;\', \';\'], \'red\');
178+
179+
const Test4 = styled(\'div\').withConfig({
180+
displayName: \'minify-single-line-comments-with-interpolations__Test4\',
181+
componentId: \'sc-1p41dy5-3\'
182+
})([\'width:100%;\']);
183+
184+
const Test5 = styled(\'div\').withConfig({
185+
displayName: \'minify-single-line-comments-with-interpolations__Test5\',
186+
componentId: \'sc-1p41dy5-4\'
187+
})([\'width:100%;\']);
188+
189+
const Test6 = styled(\'div\').withConfig({
190+
displayName: \'minify-single-line-comments-with-interpolations__Test6\',
191+
componentId: \'sc-1p41dy5-5\'
192+
})([\'background:url(\"https://google.com\");width:100%;\', \' \'], \'green\');
193+
194+
const Test7 = styled(\'div\').withConfig({
195+
displayName: \'minify-single-line-comments-with-interpolations__Test7\',
196+
componentId: \'sc-1p41dy5-6\'
197+
})([\'background:url(\"https://google.com\");width:\', \';\', \' height:\', \';\'], p => p.props.width, \'green\', p => p.props.height);"
198+
`;
199+
161200
exports[`fixtures should not use private api if not required 1`] = `
162201
"import styled from \'styled-components/no-tags\';
163202
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"plugins": [
3+
[
4+
"../../../src",
5+
{
6+
"minify": true
7+
}
8+
]
9+
]
10+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import styled from 'styled-components'
2+
3+
const Test1 = styled.div`
4+
width: 100%;
5+
// color: ${'red'};
6+
`
7+
8+
const Test2 = styled.div`
9+
width: 100%;
10+
// color: pale${'red'};
11+
`
12+
13+
const Test3 = styled.div`
14+
width: 100%;
15+
// color
16+
${'red'};
17+
`
18+
19+
const Test4 = styled.div`
20+
width: 100%;
21+
// color: ${'red'}-blue;
22+
`
23+
24+
const Test5 = styled.div`
25+
width: 100%;
26+
// color: ${'red'}${'blue'};
27+
`
28+
29+
const Test6 = styled.div`
30+
background: url("https://google.com");
31+
width: 100%;
32+
${'green'} // color: ${'red'}${'blue'};
33+
`
34+
35+
const Test7 = styled.div`
36+
background: url("https://google.com");
37+
width: ${p => p.props.width};
38+
${'green'} // color: ${'red'}${'blue'};
39+
height: ${p => p.props.height};
40+
`

test/minify/index.test.js

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,21 @@ import {
22
stripLineComment,
33
minifyRaw,
44
minifyCooked,
5-
compressSymbols
5+
compressSymbols,
66
} from '../../src/minify'
77

8+
import { makePlaceholder } from '../../src/css/placeholderUtils'
9+
810
describe('minify utils', () => {
911
describe('stripLineComment', () => {
1012
it('splits a line by potential comment starts and joins until one is an actual comment', () => {
1113
expect(stripLineComment('abc def//ghi//jkl')).toBe('abc def')
1214
})
1315

1416
it('ignores comment markers that are inside strings', () => {
15-
expect(stripLineComment('abc def"//"ghi\'//\'jkl//the end')).toBe('abc def"//"ghi\'//\'jkl')
17+
expect(stripLineComment('abc def"//"ghi\'//\'jkl//the end')).toBe(
18+
'abc def"//"ghi\'//\'jkl'
19+
)
1620
expect(stripLineComment('abc def"//"')).toBe('abc def"//"')
1721
})
1822

@@ -21,67 +25,89 @@ describe('minify utils', () => {
2125
})
2226

2327
it('ignores even unescaped URLs', () => {
24-
expect(stripLineComment('https://test.com// comment//')).toBe('https://test.com')
28+
expect(stripLineComment('https://test.com// comment//')).toBe(
29+
'https://test.com'
30+
)
2531
})
2632
})
2733

2834
describe('minify(Raw|Cooked)', () => {
2935
it('Removes multi-line comments', () => {
3036
const input = 'this is a/* ignore me please */test'
3137
const expected = 'this is a test' // NOTE: They're replaced with newlines, and newlines are joined
32-
const actual = minifyRaw(input)
38+
const [actual] = minifyRaw(input)
3339

3440
expect(actual).toBe(expected)
35-
expect(actual).toBe(minifyCooked(input))
41+
expect(actual).toBe(minifyCooked(input)[0])
3642
})
3743

3844
it('Joins all lines of code', () => {
3945
const input = 'this\nis\na/* ignore me \n please */\ntest'
4046
const expected = 'this is a test'
41-
const actual = minifyRaw(input)
47+
const [actual] = minifyRaw(input)
4248

4349
expect(actual).toBe(expected)
44-
expect(actual).toBe(minifyCooked(input))
50+
expect(actual).toBe(minifyCooked(input)[0])
4551
})
4652

4753
it('Removes line comments filling an entire line', () => {
4854
const input = 'line one\n// remove this comment\nline two'
4955
const expected = 'line one line two'
50-
const actual = minifyRaw(input)
56+
const [actual] = minifyRaw(input)
5157

5258
expect(actual).toBe(expected)
53-
expect(actual).toBe(minifyCooked(input))
59+
expect(actual).toBe(minifyCooked(input)[0])
5460
})
5561

5662
it('Removes line comments at the end of lines of code', () => {
5763
const input = 'valid line with // a comment\nout comments'
5864
const expected = 'valid line with out comments'
59-
const actual = minifyRaw(input)
65+
const [actual] = minifyRaw(input)
6066

6167
expect(actual).toBe(expected)
62-
expect(actual).toBe(minifyCooked(input))
68+
expect(actual).toBe(minifyCooked(input)[0])
6369
})
6470

6571
it('Preserves multi-line comments starting with /*!', () => {
66-
const input = 'this is a /*! dont ignore me please */ test/* but you can ignore me */'
72+
const input =
73+
'this is a /*! dont ignore me please */ test/* but you can ignore me */'
6774
const expected = 'this is a /*! dont ignore me please */ test'
68-
const actual = minifyRaw(input)
75+
const [actual] = minifyRaw(input)
76+
77+
expect(actual).toBe(expected)
78+
expect(actual).toBe(minifyCooked(input)[0])
79+
})
80+
81+
it('Returns the indices of removed placeholders (expressions)', () => {
82+
const placeholder1 = makePlaceholder(0)
83+
const placeholder2 = makePlaceholder(1)
84+
const input = `this is some\ninput with ${placeholder1} and // ignored ${placeholder2}`
85+
const expected = `this is some input with ${placeholder1} and `
86+
const [actual, indices] = minifyRaw(input)
6987

7088
expect(actual).toBe(expected)
71-
expect(actual).toBe(minifyCooked(input))
89+
expect(indices).toEqual([1])
90+
expect(minifyCooked(input)).toEqual([actual, [1]])
7291
})
7392
})
7493

7594
describe('minifyRaw', () => {
7695
it('works with raw escape codes', () => {
7796
const input = 'this\\nis\\na/* ignore me \\n please */\\ntest'
7897
const expected = 'this is a test'
79-
const actual = minifyRaw(input)
98+
const [actual] = minifyRaw(input)
99+
100+
expect(actual).toBe(expected)
101+
})
102+
})
80103

81-
expect(minifyRaw(input)).toBe(expected)
104+
describe('minifyCooked', () => {
105+
it('works with raw escape codes', () => {
106+
const input = 'this\\nis\\na/* ignore me \\n please */\\ntest'
107+
const expected = 'this\\nis\\na \\ntest'
108+
const [actual] = minifyCooked(input)
82109

83-
// NOTE: This is just a sanity check
84-
expect(minifyCooked(input)).toBe('this\\nis\\na \\ntest')
110+
expect(actual).toBe(expected)
85111
})
86112
})
87113

0 commit comments

Comments
 (0)