Skip to content

Commit 63080b1

Browse files
authored
feat: Add scope id to keyframes name (#43)
* refactor: do not include `data-v-` prefix in id * chore: bump eslint config * feat: should add scope id to keyframes name
1 parent 4626b76 commit 63080b1

File tree

5 files changed

+73
-14
lines changed

5 files changed

+73
-14
lines changed

.eslintrc.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
---
22
extends: eslint-config-ktsn
3-
parserOptions:
4-
ecmaVersion: 6
53
rules:
64
no-console: 0

lib/modules/add-scoped-id.js

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ const addScopedIdPlugin = postcss.plugin('add-scoped-id', options => {
4646

4747
if (target) {
4848
selector.insertAfter(target, selectorParser.attribute({
49-
attribute: options.id
49+
attribute: 'data-v-' + options.id
5050
}))
5151
}
5252

@@ -55,8 +55,40 @@ const addScopedIdPlugin = postcss.plugin('add-scoped-id', options => {
5555
})
5656

5757
return root => {
58-
root.walkRules(rule => {
59-
rule.selector = selectorTransformer.process(rule.selector).result
58+
const keyframes = new Map()
59+
60+
root.each(function transformNode (node) {
61+
if (node.type === 'atrule') {
62+
if (/-?keyframes$/.test(node.name)) {
63+
const before = node.params
64+
const after = node.params + '-' + options.id
65+
node.params = after
66+
keyframes.set(before, after)
67+
} else {
68+
node.each(transformNode)
69+
}
70+
} else {
71+
node.selector = selectorTransformer.process(node.selector).result
72+
}
73+
})
74+
75+
keyframes.forEach((after, before) => {
76+
root.walkDecls(decl => {
77+
if (/-?animation-name$/.test(decl.prop)) {
78+
decl.value = decl.value.split(',')
79+
.map(v => v.trim() === before ? after : v)
80+
.join(',')
81+
} else if (/-?animation$/.test(decl.prop)) {
82+
decl.value = decl.value.split(',')
83+
.map(v => {
84+
const [first, ...tail] = v.trim().split(/\s+/)
85+
return first === before
86+
? [after, ...tail].join(' ')
87+
: v
88+
})
89+
.join(',')
90+
}
91+
})
6092
})
6193
}
6294
})

lib/template-loader.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const Builder = require('./builder/builder')
1212
module.exports = function (content) {
1313
this.cacheable()
1414
const isServer = this.options.target === 'node'
15-
const id = `data-v-${genId(this.resourcePath, process.cwd())}`
15+
const id = genId(this.resourcePath, process.cwd())
1616

1717
// Acquire the query of target file
1818
const { style: stylePath } = loaderUtils.parseQuery('?' + this.request.split('?').pop())
@@ -70,7 +70,7 @@ module.exports = function (content) {
7070
// Set _scopeId if scoped CSS is enabled
7171
if (options.scoped) {
7272
builder.addLine(`
73-
options._scopeId = '${id}'
73+
options._scopeId = 'data-v-${id}'
7474
`)
7575
}
7676

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
"babel-register": "^6.24.0",
4545
"css-loader": "^0.28.0",
4646
"eslint": "^3.19.0",
47-
"eslint-config-ktsn": "^1.0.0",
47+
"eslint-config-ktsn": "^1.0.3",
4848
"extract-text-webpack-plugin": "^2.1.0",
4949
"file-loader": "^0.11.1",
5050
"html-webpack-plugin": "^2.28.0",

test/modules/add-scoped-id.spec.js

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,31 @@ const addScopedId = require('../../lib/modules/add-scoped-id')
66
describe('add scoped id module', () => {
77
it('should add scoped id into the last node for each selector', done => {
88
test(
9-
'data-v-1',
9+
'1',
1010
'h1, h2 .foo {} h3 {}',
1111
'h1[data-v-1], h2 .foo[data-v-1] {} h3[data-v-1] {}'
1212
).then(done)
1313
})
1414

1515
it('should not add scoped id into pseudo element/class', done => {
1616
test(
17-
'data-v-1',
17+
'1',
1818
'p::before {} .test:first-child {}',
1919
'p[data-v-1]::before {} .test[data-v-1]:first-child {}'
2020
).then(done)
2121
})
2222

2323
it('should add scoped id into the selectors in at-rules', done => {
2424
test(
25-
'data-v-1',
25+
'1',
2626
'@media screen { p {} }',
2727
'@media screen { p[data-v-1] {} }'
2828
).then(done)
2929
})
3030

3131
it('should generate source map', done => {
3232
test(
33-
'data-v-1',
33+
'1',
3434
'div p {}',
3535
'div p[data-v-1] {}',
3636
true
@@ -72,7 +72,7 @@ describe('add scoped id module', () => {
7272
const map = JSON.parse(Base64.decode(m[2]))
7373

7474
test(
75-
'data-v-1',
75+
'1',
7676
css,
7777
[
7878
'div p[data-v-1] {',
@@ -94,11 +94,40 @@ describe('add scoped id module', () => {
9494

9595
it('should add scope attribute the selector before >>> combinator', done => {
9696
test(
97-
'data-v-1',
97+
'1',
9898
'.foo .bar >>> .baz {}',
9999
'.foo .bar[data-v-1] .baz {}'
100100
).then(done)
101101
})
102+
103+
it('should add scope id to keyframes', done => {
104+
const id = 'abc'
105+
const input = [
106+
'.foo { animation: test 1s; }',
107+
'.bar { animation-name: test; animation-duration: 1s; }',
108+
'@keyframes test {',
109+
' 0% { opacity: 0; }',
110+
' 100% { opacity: 1; }',
111+
'}',
112+
'@-webkit-keyframes test {',
113+
' from { opacity: 0; }',
114+
' to { opacity: 1; }',
115+
'}'
116+
].join('\n')
117+
const expected = [
118+
`.foo[data-v-${id}] { animation: test-${id} 1s; }`,
119+
`.bar[data-v-${id}] { animation-name: test-${id}; animation-duration: 1s; }`,
120+
`@keyframes test-${id} {`,
121+
' 0% { opacity: 0; }',
122+
' 100% { opacity: 1; }',
123+
'}',
124+
`@-webkit-keyframes test-${id} {`,
125+
' from { opacity: 0; }',
126+
' to { opacity: 1; }',
127+
'}'
128+
].join('\n')
129+
test(id, input, expected).then(done)
130+
})
102131
})
103132

104133
function test (id, input, expected, map) {

0 commit comments

Comments
 (0)