Skip to content

Commit 9adc96b

Browse files
fix: handle all valid identifier in animation name and exclude global value (#57)
1 parent 3b3d93d commit 9adc96b

File tree

6 files changed

+1754
-1320
lines changed

6 files changed

+1754
-1320
lines changed

.github/workflows/nodejs.yml

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
name: test
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
- next
8+
pull_request:
9+
branches:
10+
- master
11+
- next
12+
13+
permissions:
14+
contents: read
15+
16+
jobs:
17+
lint:
18+
name: Lint - ${{ matrix.os }} - Node v${{ matrix.node-version }}
19+
20+
env:
21+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
22+
23+
strategy:
24+
matrix:
25+
os: [ubuntu-latest]
26+
node-version: [lts/*]
27+
28+
runs-on: ${{ matrix.os }}
29+
30+
concurrency:
31+
group: lint-${{ matrix.os }}-v${{ matrix.node-version }}-${{ github.ref }}
32+
cancel-in-progress: true
33+
34+
steps:
35+
- uses: actions/checkout@v3
36+
with:
37+
fetch-depth: 0
38+
39+
- name: Use Node.js ${{ matrix.node-version }}
40+
uses: actions/setup-node@v3
41+
with:
42+
node-version: ${{ matrix.node-version }}
43+
cache: "yarn"
44+
45+
- name: Install dependencies
46+
run: yarn --frozen-lockfile
47+
48+
- name: Lint
49+
run: yarn lint
50+
51+
test:
52+
name: Test - ${{ matrix.os }} - Node v${{ matrix.node-version }}
53+
54+
strategy:
55+
matrix:
56+
os: [ubuntu-latest, windows-latest, macos-latest]
57+
node-version: [10.x, 12.x, 14.x, 16.x, 18.x, 20.x]
58+
webpack-version: [latest]
59+
60+
runs-on: ${{ matrix.os }}
61+
62+
concurrency:
63+
group: test-${{ matrix.os }}-v${{ matrix.node-version }}-${{ github.ref }}
64+
cancel-in-progress: true
65+
66+
steps:
67+
- name: Setup Git
68+
if: matrix.os == 'windows-latest'
69+
run: git config --global core.autocrlf input
70+
71+
- uses: actions/checkout@v3
72+
73+
- name: Use Node.js ${{ matrix.node-version }}
74+
uses: actions/setup-node@v3
75+
with:
76+
node-version: ${{ matrix.node-version }}
77+
cache: "yarn"
78+
79+
- name: Install dependencies
80+
run: yarn --frozen-lockfile
81+
82+
- name: Run tests for webpack version ${{ matrix.webpack-version }}
83+
run: yarn test --ci
84+
85+
- name: Submit coverage data to codecov
86+
uses: codecov/codecov-action@v3
87+
with:
88+
token: ${{ secrets.CODECOV_TOKEN }}

.travis.yml

Lines changed: 0 additions & 8 deletions
This file was deleted.

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,8 @@
2828
"test:only": "jest",
2929
"test:watch": "jest --watch",
3030
"test:coverage": "jest --coverage --collectCoverageFrom=\"src/**/*\"",
31-
"pretest": "yarn lint",
3231
"test": "yarn test:coverage",
33-
"prepublishOnly": "yarn test"
32+
"prepublishOnly": "yarn lint && yarn test"
3433
},
3534
"dependencies": {
3635
"icss-utils": "^5.0.0",

src/index.js

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -301,10 +301,27 @@ function isWordAFunctionArgument(wordNode, functionNode) {
301301
: false;
302302
}
303303

304+
// `none` is special value, other is global values
305+
const specialKeywords = [
306+
"none",
307+
"inherit",
308+
"initial",
309+
"revert",
310+
"revert-layer",
311+
"unset",
312+
];
313+
304314
function localizeDeclarationValues(localize, declaration, context) {
305315
const valueNodes = valueParser(declaration.value);
306316

307317
valueNodes.walk((node, index, nodes) => {
318+
if (
319+
node.type === "word" &&
320+
specialKeywords.includes(node.value.toLowerCase())
321+
) {
322+
return;
323+
}
324+
308325
const subContext = {
309326
options: context.options,
310327
global: context.global,
@@ -321,16 +338,27 @@ function localizeDeclaration(declaration, context) {
321338
const isAnimation = /animation$/i.test(declaration.prop);
322339

323340
if (isAnimation) {
324-
const validIdent = /^-?[_a-z][_a-z0-9-]*$/i;
341+
// letter
342+
// An uppercase letter or a lowercase letter.
343+
//
344+
// ident-start code point
345+
// A letter, a non-ASCII code point, or U+005F LOW LINE (_).
346+
//
347+
// ident code point
348+
// An ident-start code point, a digit, or U+002D HYPHEN-MINUS (-).
349+
350+
// We don't validate `hex digits`, because we don't need it, it is work of linters.
351+
const validIdent =
352+
/^-?([a-z\u0080-\uFFFF_]|(\\[^\r\n\f])|-)((\\[^\r\n\f])|[a-z\u0080-\uFFFF_0-9-])*$/i;
325353

326354
/*
327355
The spec defines some keywords that you can use to describe properties such as the timing
328356
function. These are still valid animation names, so as long as there is a property that accepts
329357
a keyword, it is given priority. Only when all the properties that can take a keyword are
330358
exhausted can the animation name be set to the keyword. I.e.
331-
359+
332360
animation: infinite infinite;
333-
361+
334362
The animation will repeat an infinite number of times from the first argument, and will have an
335363
animation name of infinite from the second.
336364
*/
@@ -356,6 +384,8 @@ function localizeDeclaration(declaration, context) {
356384
$initial: Infinity,
357385
$inherit: Infinity,
358386
$unset: Infinity,
387+
$revert: Infinity,
388+
"$revert-layer": Infinity,
359389
};
360390

361391
const didParseAnimationName = false;

test/index.test.js

Lines changed: 100 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,91 @@ const tests = [
170170
input: ".foo { animation-name: bar, foobar; }",
171171
expected: ":local(.foo) { animation-name: :local(bar), :local(foobar); }",
172172
},
173+
{
174+
name: "not localize revert",
175+
input: ".foo { animation: revert; }",
176+
expected: ":local(.foo) { animation: revert; }",
177+
},
178+
{
179+
name: "not localize revert #2",
180+
input: ".foo { animation-name: revert; }",
181+
expected: ":local(.foo) { animation-name: revert; }",
182+
},
183+
{
184+
name: "not localize revert #3",
185+
input: ".foo { animation-name: revert, foo, none; }",
186+
expected: ":local(.foo) { animation-name: revert, :local(foo), none; }",
187+
},
188+
{
189+
name: "not localize revert-layer",
190+
input: ".foo { animation: revert-layer; }",
191+
expected: ":local(.foo) { animation: revert-layer; }",
192+
},
193+
{
194+
name: "not localize revert",
195+
input: ".foo { animation-name: revert-layer; }",
196+
expected: ":local(.foo) { animation-name: revert-layer; }",
197+
},
198+
{
199+
name: "localize animation using special characters",
200+
input: ".foo { animation: \\@bounce; }",
201+
expected: ":local(.foo) { animation: :local(\\@bounce); }",
202+
},
203+
{
204+
name: "localize animation using special characters",
205+
input: ".foo { animation: bou\\@nce; }",
206+
expected: ":local(.foo) { animation: :local(bou\\@nce); }",
207+
},
208+
{
209+
name: "localize animation using special characters",
210+
input: ".foo { animation: \\ as; }",
211+
expected: ":local(.foo) { animation: :local(\\ as); }",
212+
},
213+
{
214+
name: "localize animation using special characters",
215+
input: ".foo { animation: t\\ t; }",
216+
expected: ":local(.foo) { animation: :local(t\\ t); }",
217+
},
218+
{
219+
name: "localize animation using special characters",
220+
input: ".foo { animation: -\\a; }",
221+
expected: ":local(.foo) { animation: :local(-\\a); }",
222+
},
223+
{
224+
name: "localize animation using special characters",
225+
input: ".foo { animation: --\\a; }",
226+
expected: ":local(.foo) { animation: :local(--\\a); }",
227+
},
228+
{
229+
name: "localize animation using special characters",
230+
input: ".foo { animation: \\a; }",
231+
expected: ":local(.foo) { animation: :local(\\a); }",
232+
},
233+
{
234+
name: "localize animation using special characters",
235+
input: ".foo { animation: -\\a; }",
236+
expected: ":local(.foo) { animation: :local(-\\a); }",
237+
},
238+
{
239+
name: "localize animation using special characters",
240+
input: ".foo { animation: --; }",
241+
expected: ":local(.foo) { animation: :local(--); }",
242+
},
243+
{
244+
name: "localize animation using special characters",
245+
input: ".foo { animation: 😃bounce😃; }",
246+
expected: ":local(.foo) { animation: :local(😃bounce😃); }",
247+
},
248+
{
249+
name: "not localize revert",
250+
input: ".foo { animation: --foo; }",
251+
expected: ":local(.foo) { animation: :local(--foo); }",
252+
},
253+
{
254+
name: "localize animation",
255+
input: ".foo { animation: a; }",
256+
expected: ":local(.foo) { animation: :local(a); }",
257+
},
173258
{
174259
name: "localize animation",
175260
input: ".foo { animation: bar 5s, foobar; }",
@@ -204,23 +289,20 @@ const tests = [
204289
expected: ":local(.foo) { animation: 1s :local(foo); }",
205290
},
206291
{
207-
name:
208-
"handle animations where the first value is not the animation name whilst also using keywords",
292+
name: "handle animations where the first value is not the animation name whilst also using keywords",
209293
input: ".foo { animation: 1s normal ease-out infinite foo; }",
210294
expected:
211295
":local(.foo) { animation: 1s normal ease-out infinite :local(foo); }",
212296
},
213297
{
214-
name:
215-
"not treat animation curve as identifier of animation name even if it separated by comma",
298+
name: "not treat animation curve as identifier of animation name even if it separated by comma",
216299
input:
217300
".foo { animation: slide-right 300ms forwards ease-out, fade-in 300ms forwards ease-out; }",
218301
expected:
219302
":local(.foo) { animation: :local(slide-right) 300ms forwards ease-out, :local(fade-in) 300ms forwards ease-out; }",
220303
},
221304
{
222-
name:
223-
'not treat "start" and "end" keywords in steps() function as identifiers',
305+
name: 'not treat "start" and "end" keywords in steps() function as identifiers',
224306
input: [
225307
".foo { animation: spin 1s steps(12, end) infinite; }",
226308
".foo { animation: spin 1s STEPS(12, start) infinite; }",
@@ -303,6 +385,18 @@ const tests = [
303385
expected:
304386
"@keyframes :local(foo) { from { color: red; } to { color: blue; } }",
305387
},
388+
{
389+
name: "localize keyframes starting with special characters",
390+
input: "@keyframes \\@foo { from { color: red; } to { color: blue; } }",
391+
expected:
392+
"@keyframes :local(\\@foo) { from { color: red; } to { color: blue; } }",
393+
},
394+
{
395+
name: "localize keyframes containing special characters",
396+
input: "@keyframes f\\@oo { from { color: red; } to { color: blue; } }",
397+
expected:
398+
"@keyframes :local(f\\@oo) { from { color: red; } to { color: blue; } }",
399+
},
306400
{
307401
name: "localize keyframes in global default mode",
308402
input: "@keyframes foo {}",

0 commit comments

Comments
 (0)