Skip to content

add global for animations #76

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

Transformation examples:

Selectors (mode `local`, by default)::

<!-- prettier-ignore-start -->
```css
.foo { ... } /* => */ :local(.foo) { ... }
Expand All @@ -28,6 +30,30 @@ Transformation examples:
```
<!-- prettier-ignore-end -->

Declarations (mode `local`, by default):

<!-- prettier-ignore-start -->
```css
.foo {
animation-name: fadeInOut, global(moveLeft300px), local(bounce);
}

.bar {
animation: rotate 1s, global(spin) 3s, local(fly) 6s;
}

/* => */

:local(.foo) {
animation-name: :local(fadeInOut), moveLeft300px, :local(bounce);
}

:local(.bar) {
animation: :local(rotate) 1s, spin 3s, :local(fly) 6s;
}
```
<!-- prettier-ignore-end -->

## Building

```bash
Expand Down
142 changes: 76 additions & 66 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -347,24 +347,20 @@ function localizeDeclarationValues(localize, declaration, context) {
declaration.value = valueNodes.toString();
}

function localizeDeclaration(declaration, context) {
const isAnimation = /animation$/i.test(declaration.prop);

if (isAnimation) {
// letter
// An uppercase letter or a lowercase letter.
//
// ident-start code point
// A letter, a non-ASCII code point, or U+005F LOW LINE (_).
//
// ident code point
// An ident-start code point, a digit, or U+002D HYPHEN-MINUS (-).

// We don't validate `hex digits`, because we don't need it, it is work of linters.
const validIdent =
/^-?([a-z\u0080-\uFFFF_]|(\\[^\r\n\f])|-(?![0-9]))((\\[^\r\n\f])|[a-z\u0080-\uFFFF_0-9-])*$/i;

/*
// letter
// An uppercase letter or a lowercase letter.
//
// ident-start code point
// A letter, a non-ASCII code point, or U+005F LOW LINE (_).
//
// ident code point
// An ident-start code point, a digit, or U+002D HYPHEN-MINUS (-).

// We don't validate `hex digits`, because we don't need it, it is work of linters.
const validIdent =
/^-?([a-z\u0080-\uFFFF_]|(\\[^\r\n\f])|-(?![0-9]))((\\[^\r\n\f])|[a-z\u0080-\uFFFF_0-9-])*$/i;

/*
The spec defines some keywords that you can use to describe properties such as the timing
function. These are still valid animation names, so as long as there is a property that accepts
a keyword, it is given priority. Only when all the properties that can take a keyword are
Expand All @@ -375,48 +371,72 @@ function localizeDeclaration(declaration, context) {
The animation will repeat an infinite number of times from the first argument, and will have an
animation name of infinite from the second.
*/
const animationKeywords = {
// animation-direction
$normal: 1,
$reverse: 1,
$alternate: 1,
"$alternate-reverse": 1,
// animation-fill-mode
$forwards: 1,
$backwards: 1,
$both: 1,
// animation-iteration-count
$infinite: 1,
// animation-play-state
$paused: 1,
$running: 1,
// animation-timing-function
$ease: 1,
"$ease-in": 1,
"$ease-out": 1,
"$ease-in-out": 1,
$linear: 1,
"$step-end": 1,
"$step-start": 1,
// Special
$none: Infinity, // No matter how many times you write none, it will never be an animation name
// Global values
$initial: Infinity,
$inherit: Infinity,
$unset: Infinity,
$revert: Infinity,
"$revert-layer": Infinity,
};
const animationKeywords = {
// animation-direction
$normal: 1,
$reverse: 1,
$alternate: 1,
"$alternate-reverse": 1,
// animation-fill-mode
$forwards: 1,
$backwards: 1,
$both: 1,
// animation-iteration-count
$infinite: 1,
// animation-play-state
$paused: 1,
$running: 1,
// animation-timing-function
$ease: 1,
"$ease-in": 1,
"$ease-out": 1,
"$ease-in-out": 1,
$linear: 1,
"$step-end": 1,
"$step-start": 1,
// Special
$none: Infinity, // No matter how many times you write none, it will never be an animation name
// Global values
$initial: Infinity,
$inherit: Infinity,
$unset: Infinity,
$revert: Infinity,
"$revert-layer": Infinity,
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid creating variables every time, better performance


function localizeDeclaration(declaration, context) {
const isAnimation = /animation(-name)?$/i.test(declaration.prop);
Copy link
Member

@alexander-akait alexander-akait Nov 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simlify logic for any animation declarations and avoid code duplication


if (isAnimation) {
let parsedAnimationKeywords = {};
const valueNodes = valueParser(declaration.value).walk((node) => {
// If div-token appeared (represents as comma ','), a possibility of an animation-keywords should be reflesh.
if (node.type === "div") {
parsedAnimationKeywords = {};

return;
}
// Do not handle nested functions
else if (node.type === "function") {
} else if (
node.type === "function" &&
node.value.toLowerCase() === "local" &&
node.nodes.length === 1
) {
node.type = "word";
node.value = node.nodes[0].value;

return localizeDeclNode(node, {
Copy link
Member

@alexander-akait alexander-akait Nov 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Localize local() in global mode so syntax works for all modes

options: context.options,
global: context.global,
localizeNextItem: true,
localAliasMap: context.localAliasMap,
});
} else if (node.type === "function") {
// replace `animation: global(example)` with `animation-name: example`
if (node.value.toLowerCase() === "global" && node.nodes.length === 1) {
node.type = "word";
node.value = node.nodes[0].value;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One place for our logic for global


// Do not handle nested functions
return false;
}
// Ignore all except word
Expand All @@ -443,30 +463,20 @@ function localizeDeclaration(declaration, context) {
}
}

const subContext = {
return localizeDeclNode(node, {
options: context.options,
global: context.global,
localizeNextItem: shouldParseAnimationName && !context.global,
localAliasMap: context.localAliasMap,
};

return localizeDeclNode(node, subContext);
});
});

declaration.value = valueNodes.toString();

return;
}

const isAnimationName = /animation(-name)?$/i.test(declaration.prop);

if (isAnimationName) {
return localizeDeclarationValues(true, declaration, context);
}

const hasUrl = /url\(/i.test(declaration.value);

if (hasUrl) {
if (/url\(/i.test(declaration.value)) {
return localizeDeclarationValues(false, declaration, context);
}
}
Expand Down
62 changes: 62 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,11 @@ const tests = [
input: ".foo { animation-name: bar; }",
expected: ":local(.foo) { animation-name: :local(bar); }",
},
{
name: "localize a single animation-name #2",
input: ".foo { animation-name: local(bar); }",
expected: ":local(.foo) { animation-name: :local(bar); }",
},
{
name: "not localize animation-name in a var function",
input: ".foo { animation-name: var(--bar); }",
Expand All @@ -179,6 +184,63 @@ const tests = [
input: ".foo { animation-name: env(bar); }",
expected: ":local(.foo) { animation-name: env(bar); }",
},
{
name: "not localize animation-name in an global function",
input: ".foo { animation-name: global(bar); }",
expected: ":local(.foo) { animation-name: bar; }",
},
{
name: "localize and not localize animation-name in mixed case",
input:
".foo { animation-name: fadeInOut, global(moveLeft300px), local(bounce); }",
expected:
":local(.foo) { animation-name: :local(fadeInOut), moveLeft300px, :local(bounce); }",
},
{
name: "localize and not localize animation-name in mixed case #2",
options: { mode: "global" },
input:
".foo { animation-name: fadeInOut, global(moveLeft300px), local(bounce); }",
expected:
".foo { animation-name: fadeInOut, moveLeft300px, :local(bounce); }",
},
{
name: "localize and not localize animation-name in mixed case #3",
options: { mode: "pure" },
input:
".foo { animation-name: fadeInOut, global(moveLeft300px), local(bounce); }",
expected:
":local(.foo) { animation-name: :local(fadeInOut), moveLeft300px, :local(bounce); }",
},
{
name: "not localize animation in an global function",
input: ".foo { animation: global(bar); }",
expected: ":local(.foo) { animation: bar; }",
},
{
name: "not localize a certain animation in an global function",
input: ".foo { animation: global(bar), foo; }",
expected: ":local(.foo) { animation: bar, :local(foo); }",
},
{
name: "localize and not localize a certain animation in mixed case",
input: ".foo { animation: rotate 1s, global(spin) 3s, local(fly) 6s; }",
expected:
":local(.foo) { animation: :local(rotate) 1s, spin 3s, :local(fly) 6s; }",
},
{
name: "localize and not localize a certain animation in mixed case #2",
options: { mode: "global" },
input: ".foo { animation: rotate 1s, global(spin) 3s, local(fly) 6s; }",
expected: ".foo { animation: rotate 1s, spin 3s, :local(fly) 6s; }",
},
{
name: "localize and not localize a certain animation in mixed case #2",
options: { mode: "pure" },
input: ".foo { animation: rotate 1s, global(spin) 3s, local(fly) 6s; }",
expected:
":local(.foo) { animation: :local(rotate) 1s, spin 3s, :local(fly) 6s; }",
},
{
name: "not localize animation-name in an env function #2",
input: ".foo { animation-name: eNv(bar); }",
Expand Down
Loading