Skip to content

Commit d700119

Browse files
committed
feat: use module scoping on keyframes
1 parent 42a21a6 commit d700119

File tree

2 files changed

+78
-31
lines changed

2 files changed

+78
-31
lines changed

src/processors/mixed.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ const parser = (processor: Processor): void => {
3939
this.skip();
4040
}
4141

42+
if (node.type === 'Atrule' && node.name === 'keyframes') {
43+
processor.parseKeyframes(node);
44+
this.skip();
45+
}
46+
4247
if (node.type === 'Selector') {
4348
const classSelectors = node.children
4449
? node.children.filter((item: { type: string }) => item.type === 'ClassSelector')
@@ -87,11 +92,11 @@ const parser = (processor: Processor): void => {
8792
}
8893
});
8994
}
90-
91-
processor.parsePseudoLocalSelectors(node);
9295
}
9396

9497
processor.parseBoundVariables(node);
98+
processor.parsePseudoLocalSelectors(node);
99+
processor.storeAnimationProperties(node);
95100

96101
if (node.type === 'ClassSelector') {
97102
const generatedClassName = processor.createModuleClassname(node.name);
@@ -100,6 +105,8 @@ const parser = (processor: Processor): void => {
100105
}
101106
},
102107
});
108+
109+
processor.overwriteAnimationProperties();
103110
};
104111

105112
const mixedProcessor = async (

src/processors/processor.ts

Lines changed: 69 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ export default class Processor {
1616
public rawContent: string;
1717
public cssModuleList: CSSModuleList = {};
1818
public cssVarList: CSSModuleList = {};
19+
public cssKeyframeList: CSSModuleList = {};
20+
public cssAnimationProperties: TemplateNode[] = [];
1921
public importedCssModuleList: CSSModuleList = {};
2022

2123
public ast: Ast;
@@ -80,6 +82,31 @@ export default class Processor {
8082
this.cssModuleList[name] = value;
8183
};
8284

85+
/**
86+
* Parse component
87+
* @returns The CssModule updated component
88+
*/
89+
public parse = (): string => {
90+
if (
91+
this.options.parseStyleTag &&
92+
(hasModuleAttribute(this.ast) || (this.options.useAsDefaultScoping && this.ast.css))
93+
) {
94+
this.isParsingImports = false;
95+
this.styleParser(this);
96+
}
97+
98+
if (this.options.parseExternalStylesheet && hasModuleImports(this.rawContent)) {
99+
this.isParsingImports = true;
100+
parseImportDeclaration(this);
101+
}
102+
103+
if (Object.keys(this.cssModuleList).length > 0 || Object.keys(this.cssVarList).length > 0) {
104+
parseTemplate(this);
105+
}
106+
107+
return this.magicContent.toString();
108+
};
109+
83110
/**
84111
* Parse css dynamic variables bound to js bind()
85112
* @param node The ast "Selector" node to parse
@@ -113,45 +140,58 @@ export default class Processor {
113140
};
114141

115142
/**
116-
* Parse pseudo selector :local()
143+
* Parse keyframes
117144
* @param node The ast "Selector" node to parse
118145
*/
119-
public parsePseudoLocalSelectors = (node: TemplateNode): void => {
120-
const pseudoLocalSelectors =
121-
node.children?.filter(
122-
(item) => item.type === 'PseudoClassSelector' && item.name === 'local'
123-
) ?? [];
124-
125-
if (pseudoLocalSelectors.length > 0) {
126-
pseudoLocalSelectors.forEach((item) => {
127-
this.magicContent.remove(item.start, item.start + `:local(`.length);
128-
this.magicContent.remove(item.end - 1, item.end);
129-
});
146+
public parseKeyframes = (node: TemplateNode): void => {
147+
const rulePrelude = node.prelude.children[0];
148+
if (rulePrelude.name.indexOf('-global-') === -1) {
149+
const animationName = this.createModuleClassname(rulePrelude.name);
150+
this.magicContent.overwrite(rulePrelude.start, rulePrelude.end, `-global-${animationName}`);
151+
this.cssKeyframeList[rulePrelude.name] = animationName;
130152
}
131153
};
132154

133155
/**
134-
* Parse component
135-
* @returns The CssModule updated component
156+
* Parse pseudo selector :local()
157+
* @param node The ast "Selector" node to parse
136158
*/
137-
public parse = (): string => {
138-
if (
139-
this.options.parseStyleTag &&
140-
(hasModuleAttribute(this.ast) || (this.options.useAsDefaultScoping && this.ast.css))
141-
) {
142-
this.isParsingImports = false;
143-
this.styleParser(this);
144-
}
145-
146-
if (this.options.parseExternalStylesheet && hasModuleImports(this.rawContent)) {
147-
this.isParsingImports = true;
148-
parseImportDeclaration(this);
159+
public parsePseudoLocalSelectors = (node: TemplateNode): void => {
160+
if (node.type === 'PseudoClassSelector' && node.name === 'local') {
161+
this.magicContent.remove(node.start, node.start + `:local(`.length);
162+
this.magicContent.remove(node.end - 1, node.end);
149163
}
164+
};
150165

151-
if (Object.keys(this.cssModuleList).length > 0 || Object.keys(this.cssVarList).length > 0) {
152-
parseTemplate(this);
166+
/**
167+
* Store animation properties
168+
* @param node The ast "Selector" node to parse
169+
*/
170+
public storeAnimationProperties = (node: TemplateNode): void => {
171+
if (node.type === 'Declaration' && node.property === 'animation') {
172+
let names = 0;
173+
let properties = 0;
174+
node.value.children.forEach((item: TemplateNode) => {
175+
if (item.type === 'Identifier' && properties === names) {
176+
names += 1;
177+
this.cssAnimationProperties.push(item);
178+
}
179+
if (item.type === 'Operator' && item.value === ',') {
180+
properties += 1;
181+
}
182+
});
153183
}
184+
};
154185

155-
return this.magicContent.toString();
186+
/**
187+
* Overwrite animation properties
188+
* apply module when required
189+
*/
190+
public overwriteAnimationProperties = (): void => {
191+
this.cssAnimationProperties.forEach((item) => {
192+
if (item.name in this.cssKeyframeList) {
193+
this.magicContent.overwrite(item.start, item.end, this.cssKeyframeList[item.name]);
194+
}
195+
});
156196
};
157197
}

0 commit comments

Comments
 (0)