• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SAP / less-openui5 / 13906243876

17 Mar 2025 05:43PM UTC coverage: 94.719%. Remained the same
13906243876

Pull #391

github

web-flow
Merge dd2a4f8ee into b8693f39a
Pull Request #391: Bump actions/setup-node from 4.2.0 to 4.3.0

501 of 560 branches covered (89.46%)

Branch coverage included in aggregate %.

916 of 936 relevant lines covered (97.86%)

206.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.22
/lib/plugin/css-variables-collector.js
1
"use strict";
2

3
const less = require("../thirdparty/less");
1✔
4

5
const CSSVariablesCollectorPlugin = module.exports = function(config) {
1✔
6
        this.config = config;
10✔
7
        // eslint-disable-next-line new-cap
8
        this.native = new less.tree.visitor(this);
10✔
9
        this.vars = {};
10✔
10
        this.calcVars = {};
10✔
11
        this.ruleStack = [];
10✔
12
        this.mixinStack = [];
10✔
13
        this.parenStack = [];
10✔
14
        this.fontFaceDirectiveStack = [];
10✔
15
};
16

17
CSSVariablesCollectorPlugin.prototype = {
1✔
18

19
        // needed to keep the less variable references intact to use this info for the CSS variables references
20
        isPreEvalVisitor: true,
21

22
        isReplacing: true,
23

24
        _isInMixinOrParenOrFontFaceDirective() {
25
                return this.mixinStack.length > 0 || this.parenStack.length > 0 || this.fontFaceDirectiveStack.length > 0;
222✔
26
        },
27

28
        _isVarInRule() {
29
                return this.ruleStack.length > 0 && !this.ruleStack[this.ruleStack.length - 1].variable;
72✔
30
        },
31

32
        _isVarInLibrary({filename} = {}) {
2✔
33
                // for libraries we check that the file is within the libraries theme path
34
                // in all other cases with no filename (indicates calculated variables)
35
                // or in case of variables in standalone less files we just include them!
36
                const regex = new RegExp(`(^|/)${this.config.libPath}/themes/`);
60✔
37
                const include = !filename ||
60✔
38
                        (this.config.libPath ? regex.test(filename) : true);
58✔
39
                return include;
60✔
40
        },
41

42
        _isRelevant() {
43
                return !this._isInMixinOrParenOrFontFaceDirective() && this._isVarInRule();
94✔
44
        },
45

46
        toLessVariables(varsOverride) {
47
                const vars = {};
5✔
48
                Object.keys(this.vars).forEach((variableName) => {
5✔
49
                        const override = this.vars[variableName].updateAfterEval && varsOverride[variableName] !== undefined;
24✔
50
                        vars[variableName] = {
24✔
51
                                css: override ? varsOverride[variableName] : this.vars[variableName].css,
24✔
52
                                export: this.vars[variableName].export
53
                        };
54
                });
55
                let lessVariables = "";
5✔
56
                Object.keys(vars).forEach((variableName) => {
5✔
57
                        const variableValue = vars[variableName].css;
24✔
58
                        lessVariables += `@${variableName}: ${variableValue};\n`;
24✔
59
                });
60
                Object.keys(this.calcVars).forEach((variableName) => {
5✔
61
                        const variableValue = this.calcVars[variableName].css;
1✔
62
                        lessVariables += `@${variableName}: ${variableValue};\n`;
1✔
63
                });
64
                lessVariables += "\n:root {\n";
5✔
65
                Object.keys(vars).forEach((variableName) => {
5✔
66
                        if (vars[variableName].export) {
24!
67
                                lessVariables += `--${variableName}: @${variableName};\n`;
24✔
68
                        }
69
                });
70
                Object.keys(this.calcVars).forEach((variableName) => {
5✔
71
                        if (this.calcVars[variableName].export) {
1!
72
                                lessVariables += `--${variableName}: @${variableName};\n`;
1✔
73
                        }
74
                });
75
                lessVariables += "}\n";
5✔
76
                return lessVariables;
5✔
77
        },
78

79
        _getCSS(node) {
80
                let css = "";
60✔
81

82
                // override: do not evaluate variables
83
                less.tree.Variable.prototype.genCSS = function(env, output) {
60✔
84
                        new less.tree.Anonymous(this.name, this.index, this.currentFileInfo, this.mapLines).genCSS(env, output);
16✔
85
                };
86
                // override: keep quoting for less variables
87
                const fnQuotedgenCSS = less.tree.Quoted.prototype.genCSS;
60✔
88
                less.tree.Quoted.prototype.genCSS = function(env, output) {
60✔
89
                        new less.tree.Anonymous((this.escaped ? "~" : "") + this.quote + this.value + this.quote, this.index, this.currentFileInfo, this.mapLines).genCSS(env, output);
10!
90
                };
91

92
                // add the variable declaration to the list of vars
93
                css = node.toCSS();
60✔
94

95
                // reset overrides
96
                less.tree.Variable.prototype.genCSS = undefined;
60✔
97
                less.tree.Quoted.prototype.genCSS = fnQuotedgenCSS;
60✔
98

99
                return css;
60✔
100
        },
101

102
        run(root) {
103
                return this.native.visit(root);
10✔
104
        },
105

106
        visitOperation(node, visitArgs) {
107
                if (this._isRelevant()) {
3✔
108
                        // console.log("visitOperation", this.ruleStack[this.ruleStack.length - 1], this._getCSS(node));
109
                        return new less.tree.Call("calc", [new less.tree.Expression([node.operands[0], new less.tree.Anonymous(node.op), node.operands[1]])]);
1✔
110
                }
111
                return node;
2✔
112
        },
113

114
        visitCall(node, visitArgs) {
115
                // if variables are used inside rules, generate a new calculated variable for it!
116
                const isRelevantFunction = typeof less.tree.functions[node.name] === "function" && ["rgba"].indexOf(node.name) === -1;
42✔
117
                if (this._isRelevant() && isRelevantFunction) {
42✔
118
                        // console.log("visitCall", this.ruleStack[this.ruleStack.length - 1], this._getCSS(node));
119
                        const css = this._getCSS(node);
2✔
120
                        let newName = this.config.prefix + "function_" + node.name + Object.keys(this.vars).length;
2✔
121
                        // check for duplicate value in vars already
122
                        for (const name in this.calcVars) {
2✔
123
                                if (this.calcVars[name].css === css) {
1!
124
                                        newName = name;
1✔
125
                                        break;
1✔
126
                                }
127
                        }
128
                        this.calcVars[newName] = {
2✔
129
                                css: css,
130
                                export: this._isVarInLibrary()
131
                        };
132
                        return new less.tree.Call("var", [new less.tree.Anonymous("--" + newName, node.index, node.currentFileInfo, node.mapLines)]);
2✔
133
                }
134
                return node;
40✔
135
        },
136

137
        visitNegative(node, visitArgs) {
138
                // convert negative into calc function
139
                if (this._isRelevant()) {
3✔
140
                        // console.log("visitNegative", this.ruleStack[this.ruleStack.length - 1], this._getCSS(node));
141
                        return new less.tree.Call("calc", [new less.tree.Expression([new less.tree.Anonymous("-1"), new less.tree.Anonymous("*"), node.value])]);
1✔
142
                }
143
                return node;
2✔
144
        },
145

146
        visitVariable(node, visitArgs) {
147
                // convert less variables into CSS variables
148
                if (this._isRelevant()) {
46✔
149
                        return new less.tree.Call("var", [new less.tree.Anonymous(node.name.replace(/^@/, "--"), node.index, node.currentFileInfo, node.mapLines)]);
16✔
150
                }
151
                return node;
30✔
152
        },
153

154
        visitRule(node, visitArgs) {
155
                // check rule for being a variable declaration
156
                const isVarDeclaration = typeof node.name === "string" && node.name.startsWith("@");
128✔
157
                if (!this._isInMixinOrParenOrFontFaceDirective() && isVarDeclaration) {
128✔
158
                        // add the variable declaration to the list of vars
159
                        const varName = node.name.substr(1);
58✔
160
                        const isVarInLib = this._isVarInLibrary({
58✔
161
                                filename: node.currentFileInfo.filename
162
                        });
163
                        this.vars[varName] = {
58✔
164
                                css: this._getCSS(node.value),
165
                                export: isVarInLib
166
                        };
167
                }
168
                // store the rule context for the call variable extraction
169
                this.ruleStack.push(node);
128✔
170
                return node;
128✔
171
        },
172

173
        visitRuleOut(node) {
174
                // remove rule context
175
                this.ruleStack.pop();
128✔
176
                return node;
128✔
177
        },
178

179
        visitMixinDefinition(node, visitArgs) {
180
                // store the mixin context
181
                this.mixinStack.push(node);
4✔
182
                return node;
4✔
183
        },
184

185
        visitMixinDefinitionOut(node) {
186
                // remove mixin context
187
                this.mixinStack.pop();
4✔
188
                return node;
4✔
189
        },
190

191
        visitParen(node, visitArgs) {
192
                // store the parenthesis context
193
                this.parenStack.push(node);
2✔
194
                return node;
2✔
195
        },
196

197
        visitParenOut(node) {
198
                // remove parenthesis context
199
                this.parenStack.pop();
2✔
200
                return node;
2✔
201
        },
202

203
        visitDirective(node, visitArgs) {
204
                // store the @font-face directive context
205
                if (node.name === "@font-face") {
4✔
206
                        this.fontFaceDirectiveStack.push(node);
2✔
207
                }
208
                return node;
4✔
209
        },
210

211
        visitDirectiveOut(node) {
212
                // remove @font-face directive context
213
                if (node.name === "@font-face") {
4✔
214
                        this.fontFaceDirectiveStack.pop();
2✔
215
                }
216
                return node;
4✔
217
        },
218

219
        visitUrl(node, visitArgs) {
220
                // we mark the less variables which should be updated after eval
221
                // => strangewise less variables with "none" values are also urls
222
                //    after the less variables have been evaluated
223
                if (this.ruleStack.length > 0 && this.ruleStack[0].variable) {
16✔
224
                        this.vars[this.ruleStack[0].name.substr(1)].updateAfterEval = true;
10✔
225
                }
226
                return node;
16✔
227
        }
228

229
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc