less 4.9.0 → 5.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -5
- package/bin/lessc +190 -630
- package/dist/less-node.cjs +399 -14570
- package/index.cjs +5 -2
- package/lib/index.js +164 -0
- package/lib/lessc-helper.js +52 -0
- package/lib/logger.js +76 -0
- package/lib/options.d.ts +46 -0
- package/lib/options.js +145 -0
- package/lib/types.js +20 -0
- package/lib/version.js +19 -0
- package/package.json +34 -40
- package/dist/less.cjs +0 -15215
- package/dist/less.js +0 -15215
- package/dist/less.min.js +0 -11
- package/dist/less.min.js.map +0 -1
- package/lib/less/constants.js +0 -13
- package/lib/less/contexts.js +0 -165
- package/lib/less/data/colors.js +0 -150
- package/lib/less/data/index.js +0 -4
- package/lib/less/data/unit-conversions.js +0 -21
- package/lib/less/default-options.js +0 -89
- package/lib/less/deprecation.js +0 -82
- package/lib/less/environment/abstract-file-manager.js +0 -140
- package/lib/less/environment/abstract-plugin-loader.js +0 -185
- package/lib/less/environment/environment-api.ts +0 -21
- package/lib/less/environment/environment.js +0 -59
- package/lib/less/environment/file-manager-api.ts +0 -77
- package/lib/less/functions/boolean.js +0 -29
- package/lib/less/functions/color-blending.js +0 -85
- package/lib/less/functions/color.js +0 -452
- package/lib/less/functions/data-uri.js +0 -74
- package/lib/less/functions/default.js +0 -26
- package/lib/less/functions/function-caller.js +0 -55
- package/lib/less/functions/function-registry.js +0 -36
- package/lib/less/functions/index.js +0 -35
- package/lib/less/functions/list.js +0 -158
- package/lib/less/functions/math-helper.js +0 -22
- package/lib/less/functions/math.js +0 -30
- package/lib/less/functions/number.js +0 -95
- package/lib/less/functions/string.js +0 -39
- package/lib/less/functions/style.js +0 -29
- package/lib/less/functions/svg.js +0 -87
- package/lib/less/functions/types.js +0 -70
- package/lib/less/import-manager.js +0 -183
- package/lib/less/index.js +0 -95
- package/lib/less/less-error.js +0 -166
- package/lib/less/logger.js +0 -34
- package/lib/less/parse-tree.js +0 -124
- package/lib/less/parse.js +0 -87
- package/lib/less/parser/lookup-pattern.js +0 -26
- package/lib/less/parser/parser-input.js +0 -400
- package/lib/less/parser/parser.js +0 -2998
- package/lib/less/plugin-manager.js +0 -168
- package/lib/less/render.js +0 -41
- package/lib/less/source-map-builder.js +0 -82
- package/lib/less/source-map-output.js +0 -151
- package/lib/less/transform-tree.js +0 -102
- package/lib/less/tree/anonymous.js +0 -61
- package/lib/less/tree/assignment.js +0 -48
- package/lib/less/tree/atrule-syntax.js +0 -8
- package/lib/less/tree/atrule.js +0 -340
- package/lib/less/tree/attribute.js +0 -63
- package/lib/less/tree/call.js +0 -132
- package/lib/less/tree/color.js +0 -285
- package/lib/less/tree/combinator.js +0 -38
- package/lib/less/tree/comment.js +0 -48
- package/lib/less/tree/condition.js +0 -65
- package/lib/less/tree/container.js +0 -104
- package/lib/less/tree/debug-info.js +0 -80
- package/lib/less/tree/declaration.js +0 -139
- package/lib/less/tree/detached-ruleset.js +0 -45
- package/lib/less/tree/dimension.js +0 -215
- package/lib/less/tree/element.js +0 -93
- package/lib/less/tree/expression.js +0 -100
- package/lib/less/tree/extend.js +0 -88
- package/lib/less/tree/import.js +0 -299
- package/lib/less/tree/index.js +0 -56
- package/lib/less/tree/interpolated-variable.js +0 -106
- package/lib/less/tree/javascript.js +0 -45
- package/lib/less/tree/js-eval-node.js +0 -80
- package/lib/less/tree/keyword.js +0 -28
- package/lib/less/tree/media.js +0 -90
- package/lib/less/tree/merge-rules.js +0 -47
- package/lib/less/tree/mixin-call.js +0 -283
- package/lib/less/tree/mixin-definition.js +0 -319
- package/lib/less/tree/namespace-value.js +0 -95
- package/lib/less/tree/negative.js +0 -37
- package/lib/less/tree/nested-at-rule.js +0 -239
- package/lib/less/tree/node.js +0 -310
- package/lib/less/tree/operation.js +0 -81
- package/lib/less/tree/paren.js +0 -41
- package/lib/less/tree/property.js +0 -96
- package/lib/less/tree/query-in-parens.js +0 -64
- package/lib/less/tree/quoted.js +0 -131
- package/lib/less/tree/ruleset.js +0 -1068
- package/lib/less/tree/selector.js +0 -201
- package/lib/less/tree/unicode-descriptor.js +0 -14
- package/lib/less/tree/unit.js +0 -170
- package/lib/less/tree/url.js +0 -83
- package/lib/less/tree/value.js +0 -60
- package/lib/less/tree/variable-call.js +0 -60
- package/lib/less/tree/variable.js +0 -85
- package/lib/less/utils.js +0 -126
- package/lib/less/visitors/extend-visitor.js +0 -505
- package/lib/less/visitors/import-sequencer.js +0 -56
- package/lib/less/visitors/import-visitor.js +0 -203
- package/lib/less/visitors/index.js +0 -15
- package/lib/less/visitors/join-selector-visitor.js +0 -65
- package/lib/less/visitors/set-tree-visibility-visitor.js +0 -57
- package/lib/less/visitors/to-css-visitor.js +0 -332
- package/lib/less/visitors/visitor.js +0 -165
- package/lib/less-browser/add-default-options.js +0 -53
- package/lib/less-browser/bootstrap.js +0 -67
- package/lib/less-browser/browser.js +0 -65
- package/lib/less-browser/cache.js +0 -43
- package/lib/less-browser/error-reporting.js +0 -170
- package/lib/less-browser/file-manager.js +0 -112
- package/lib/less-browser/image-size.js +0 -28
- package/lib/less-browser/index.js +0 -284
- package/lib/less-browser/log-listener.js +0 -42
- package/lib/less-browser/plugin-loader.js +0 -24
- package/lib/less-browser/utils.js +0 -30
- package/lib/less-node/environment.js +0 -43
- package/lib/less-node/file-manager.js +0 -156
- package/lib/less-node/fs.js +0 -14
- package/lib/less-node/image-size.js +0 -78
- package/lib/less-node/index.js +0 -31
- package/lib/less-node/lessc-helper.js +0 -95
- package/lib/less-node/plugin-loader.js +0 -62
- package/lib/less-node/url-file-manager.js +0 -60
package/lib/less/tree/ruleset.js
DELETED
|
@@ -1,1068 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
/** @import { EvalContext, CSSOutput, TreeVisitor, FileInfo, VisibilityInfo } from './node.js' */
|
|
3
|
-
/** @import { FunctionRegistry } from './nested-at-rule.js' */
|
|
4
|
-
import Node from './node.js';
|
|
5
|
-
import Declaration from './declaration.js';
|
|
6
|
-
import Keyword from './keyword.js';
|
|
7
|
-
import Comment from './comment.js';
|
|
8
|
-
import Paren from './paren.js';
|
|
9
|
-
import Selector from './selector.js';
|
|
10
|
-
import Element from './element.js';
|
|
11
|
-
import Anonymous from './anonymous.js';
|
|
12
|
-
import contexts from '../contexts.js';
|
|
13
|
-
import globalFunctionRegistry from '../functions/function-registry.js';
|
|
14
|
-
import defaultFunc from '../functions/default.js';
|
|
15
|
-
import getDebugInfo from './debug-info.js';
|
|
16
|
-
import * as utils from '../utils.js';
|
|
17
|
-
import Parser from '../parser/parser.js';
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* @typedef {Node & {
|
|
21
|
-
* rules?: Node[],
|
|
22
|
-
* selectors?: Selector[],
|
|
23
|
-
* root?: boolean,
|
|
24
|
-
* firstRoot?: boolean,
|
|
25
|
-
* allowImports?: boolean,
|
|
26
|
-
* functionRegistry?: FunctionRegistry,
|
|
27
|
-
* originalRuleset?: Node,
|
|
28
|
-
* debugInfo?: { lineNumber: number, fileName: string },
|
|
29
|
-
* evalFirst?: boolean,
|
|
30
|
-
* isRuleset?: boolean,
|
|
31
|
-
* isCharset?: () => boolean,
|
|
32
|
-
* merge?: boolean | string,
|
|
33
|
-
* multiMedia?: boolean,
|
|
34
|
-
* parse?: { context: EvalContext, importManager: object },
|
|
35
|
-
* bubbleSelectors?: (selectors: Selector[]) => void
|
|
36
|
-
* }} RuleNode
|
|
37
|
-
*/
|
|
38
|
-
|
|
39
|
-
class Ruleset extends Node {
|
|
40
|
-
get type() { return 'Ruleset'; }
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* @param {Selector[] | null} selectors
|
|
44
|
-
* @param {Node[] | null} rules
|
|
45
|
-
* @param {boolean} [strictImports]
|
|
46
|
-
* @param {VisibilityInfo} [visibilityInfo]
|
|
47
|
-
*/
|
|
48
|
-
constructor(selectors, rules, strictImports, visibilityInfo) {
|
|
49
|
-
super();
|
|
50
|
-
/** @type {Selector[] | null} */
|
|
51
|
-
this.selectors = selectors;
|
|
52
|
-
/** @type {Node[] | null} */
|
|
53
|
-
this.rules = rules;
|
|
54
|
-
/** @type {Object<string, { rule: Node, path: Node[] }[]>} */
|
|
55
|
-
this._lookups = {};
|
|
56
|
-
/** @type {Object<string, Declaration> | null} */
|
|
57
|
-
this._variables = null;
|
|
58
|
-
/** @type {Object<string, Declaration[]> | null} */
|
|
59
|
-
this._properties = null;
|
|
60
|
-
/** @type {boolean | undefined} */
|
|
61
|
-
this.strictImports = strictImports;
|
|
62
|
-
this.copyVisibilityInfo(visibilityInfo);
|
|
63
|
-
this.allowRoot = true;
|
|
64
|
-
/** @type {boolean} */
|
|
65
|
-
this.isRuleset = true;
|
|
66
|
-
|
|
67
|
-
/** @type {boolean | undefined} */
|
|
68
|
-
this.root = undefined;
|
|
69
|
-
/** @type {boolean | undefined} */
|
|
70
|
-
this.firstRoot = undefined;
|
|
71
|
-
/** @type {boolean | undefined} */
|
|
72
|
-
this.allowImports = undefined;
|
|
73
|
-
/** @type {FunctionRegistry | undefined} */
|
|
74
|
-
this.functionRegistry = undefined;
|
|
75
|
-
/** @type {Node | undefined} */
|
|
76
|
-
this.originalRuleset = undefined;
|
|
77
|
-
/** @type {{ lineNumber: number, fileName: string } | undefined} */
|
|
78
|
-
this.debugInfo = undefined;
|
|
79
|
-
/** @type {Selector[][] | undefined} */
|
|
80
|
-
this.paths = undefined;
|
|
81
|
-
/** @type {Ruleset[] | null | undefined} */
|
|
82
|
-
this._rulesets = undefined;
|
|
83
|
-
/** @type {boolean | undefined} */
|
|
84
|
-
this.evalFirst = undefined;
|
|
85
|
-
this.setParent(this.selectors, this);
|
|
86
|
-
this.setParent(this.rules, this);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
isRulesetLike() { return true; }
|
|
90
|
-
|
|
91
|
-
/** @param {TreeVisitor} visitor */
|
|
92
|
-
accept(visitor) {
|
|
93
|
-
if (this.paths) {
|
|
94
|
-
this.paths = /** @type {Selector[][]} */ (/** @type {unknown} */ (visitor.visitArray(/** @type {Node[]} */ (/** @type {unknown} */ (this.paths)), true)));
|
|
95
|
-
} else if (this.selectors) {
|
|
96
|
-
this.selectors = /** @type {Selector[]} */ (visitor.visitArray(this.selectors));
|
|
97
|
-
}
|
|
98
|
-
if (this.rules && this.rules.length) {
|
|
99
|
-
this.rules = visitor.visitArray(this.rules);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/** @param {EvalContext} context */
|
|
104
|
-
eval(context) {
|
|
105
|
-
/** @type {Selector[] | undefined} */
|
|
106
|
-
let selectors;
|
|
107
|
-
/** @type {number} */
|
|
108
|
-
let selCnt;
|
|
109
|
-
/** @type {Selector} */
|
|
110
|
-
let selector;
|
|
111
|
-
/** @type {number} */
|
|
112
|
-
let i;
|
|
113
|
-
/** @type {boolean | undefined} */
|
|
114
|
-
let hasVariable;
|
|
115
|
-
let hasOnePassingSelector = false;
|
|
116
|
-
|
|
117
|
-
if (this.selectors && (selCnt = this.selectors.length)) {
|
|
118
|
-
selectors = new Array(selCnt);
|
|
119
|
-
defaultFunc.error({
|
|
120
|
-
type: 'Syntax',
|
|
121
|
-
message: 'it is currently only allowed in parametric mixin guards,'
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
for (i = 0; i < selCnt; i++) {
|
|
125
|
-
selector = /** @type {Selector} */ (this.selectors[i].eval(context));
|
|
126
|
-
for (let j = 0; j < selector.elements.length; j++) {
|
|
127
|
-
if (selector.elements[j].isVariable) {
|
|
128
|
-
hasVariable = true;
|
|
129
|
-
break;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
selectors[i] = selector;
|
|
133
|
-
if (selector.evaldCondition) {
|
|
134
|
-
hasOnePassingSelector = true;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (hasVariable) {
|
|
139
|
-
const toParseSelectors = new Array(selCnt);
|
|
140
|
-
for (i = 0; i < selCnt; i++) {
|
|
141
|
-
selector = selectors[i];
|
|
142
|
-
toParseSelectors[i] = selector.toCSS(context);
|
|
143
|
-
}
|
|
144
|
-
const startingIndex = selectors[0].getIndex();
|
|
145
|
-
const selectorFileInfo = selectors[0].fileInfo();
|
|
146
|
-
new (/** @type {new (...args: [EvalContext, object, FileInfo, number]) => { parseNode: Function }} */ (/** @type {unknown} */ (Parser)))(context, /** @type {{ context: EvalContext, importManager: object }} */ (this.parse).importManager, selectorFileInfo, startingIndex).parseNode(
|
|
147
|
-
toParseSelectors.join(','),
|
|
148
|
-
['selectors'],
|
|
149
|
-
function(/** @type {Error | null} */ err, /** @type {Node[]} */ result) {
|
|
150
|
-
if (result) {
|
|
151
|
-
selectors = /** @type {Selector[]} */ (utils.flattenArray(result));
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
defaultFunc.reset();
|
|
157
|
-
} else {
|
|
158
|
-
hasOnePassingSelector = true;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
let rules = this.rules ? utils.copyArray(this.rules) : null;
|
|
162
|
-
const ruleset = new Ruleset(selectors, rules, this.strictImports, this.visibilityInfo());
|
|
163
|
-
/** @type {Node} */
|
|
164
|
-
let rule;
|
|
165
|
-
/** @type {Node} */
|
|
166
|
-
let subRule;
|
|
167
|
-
|
|
168
|
-
ruleset.originalRuleset = this;
|
|
169
|
-
ruleset.root = this.root;
|
|
170
|
-
ruleset.firstRoot = this.firstRoot;
|
|
171
|
-
ruleset.allowImports = this.allowImports;
|
|
172
|
-
|
|
173
|
-
if (this.debugInfo) {
|
|
174
|
-
ruleset.debugInfo = this.debugInfo;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (!hasOnePassingSelector) {
|
|
178
|
-
/** @type {Node[]} */ (rules).length = 0;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// push the current ruleset to the frames stack
|
|
182
|
-
const ctxFrames = context.frames;
|
|
183
|
-
|
|
184
|
-
// inherit a function registry from the frames stack when possible;
|
|
185
|
-
// otherwise from the global registry
|
|
186
|
-
/** @type {FunctionRegistry | undefined} */
|
|
187
|
-
let foundRegistry;
|
|
188
|
-
for (let fi = 0, fn = ctxFrames.length; fi !== fn; ++fi) {
|
|
189
|
-
foundRegistry = /** @type {RuleNode} */ (ctxFrames[fi]).functionRegistry;
|
|
190
|
-
if (foundRegistry) { break; }
|
|
191
|
-
}
|
|
192
|
-
ruleset.functionRegistry = (foundRegistry || globalFunctionRegistry).inherit();
|
|
193
|
-
ctxFrames.unshift(ruleset);
|
|
194
|
-
|
|
195
|
-
// currrent selectors
|
|
196
|
-
/** @type {Selector[][] | undefined} */
|
|
197
|
-
let ctxSelectors = /** @type {EvalContext & { selectors?: Selector[][] }} */ (context).selectors;
|
|
198
|
-
if (!ctxSelectors) {
|
|
199
|
-
/** @type {EvalContext & { selectors?: Selector[][] }} */ (context).selectors = ctxSelectors = [];
|
|
200
|
-
}
|
|
201
|
-
ctxSelectors.unshift(this.selectors);
|
|
202
|
-
|
|
203
|
-
// Evaluate imports
|
|
204
|
-
if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) {
|
|
205
|
-
ruleset.evalImports(context);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Store the frames around mixin definitions,
|
|
209
|
-
// so they can be evaluated like closures when the time comes.
|
|
210
|
-
const rsRules = /** @type {Node[]} */ (ruleset.rules);
|
|
211
|
-
for (i = 0; (rule = rsRules[i]); i++) {
|
|
212
|
-
if (/** @type {RuleNode} */ (rule).evalFirst) {
|
|
213
|
-
rsRules[i] = rule.eval(context);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const mediaBlockCount = (context.mediaBlocks && context.mediaBlocks.length) || 0;
|
|
218
|
-
|
|
219
|
-
// Evaluate mixin calls.
|
|
220
|
-
for (i = 0; (rule = rsRules[i]); i++) {
|
|
221
|
-
if (rule.type === 'MixinCall') {
|
|
222
|
-
/* jshint loopfunc:true */
|
|
223
|
-
rules = /** @type {Node[]} */ (/** @type {unknown} */ (rule.eval(context))).filter(function(/** @type {Node & { variable?: boolean }} */ r) {
|
|
224
|
-
if ((r instanceof Declaration) && r.variable) {
|
|
225
|
-
// do not pollute the scope if the variable is
|
|
226
|
-
// already there. consider returning false here
|
|
227
|
-
// but we need a way to "return" variable from mixins
|
|
228
|
-
return !(ruleset.variable(/** @type {string} */ (r.name)));
|
|
229
|
-
}
|
|
230
|
-
return true;
|
|
231
|
-
});
|
|
232
|
-
rsRules.splice.apply(rsRules, /** @type {[number, number, ...Node[]]} */ ([i, 1].concat(rules)));
|
|
233
|
-
i += rules.length - 1;
|
|
234
|
-
ruleset.resetCache();
|
|
235
|
-
} else if (rule.type === 'VariableCall') {
|
|
236
|
-
/* jshint loopfunc:true */
|
|
237
|
-
rules = /** @type {Node[]} */ (/** @type {RuleNode} */ (rule.eval(context)).rules).filter(function(/** @type {Node & { variable?: boolean }} */ r) {
|
|
238
|
-
if ((r instanceof Declaration) && r.variable) {
|
|
239
|
-
// do not pollute the scope at all
|
|
240
|
-
return false;
|
|
241
|
-
}
|
|
242
|
-
return true;
|
|
243
|
-
});
|
|
244
|
-
rsRules.splice.apply(rsRules, /** @type {[number, number, ...Node[]]} */ ([i, 1].concat(rules)));
|
|
245
|
-
i += rules.length - 1;
|
|
246
|
-
ruleset.resetCache();
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Evaluate everything else
|
|
251
|
-
for (i = 0; (rule = rsRules[i]); i++) {
|
|
252
|
-
if (!/** @type {RuleNode} */ (rule).evalFirst) {
|
|
253
|
-
rsRules[i] = rule = rule.eval ? rule.eval(context) : rule;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// Evaluate everything else
|
|
258
|
-
for (i = 0; (rule = rsRules[i]); i++) {
|
|
259
|
-
// for rulesets, check if it is a css guard and can be removed
|
|
260
|
-
if (rule instanceof Ruleset && rule.selectors && rule.selectors.length === 1) {
|
|
261
|
-
// check if it can be folded in (e.g. & where)
|
|
262
|
-
if (rule.selectors[0] && rule.selectors[0].isJustParentSelector()) {
|
|
263
|
-
rsRules.splice(i--, 1);
|
|
264
|
-
|
|
265
|
-
for (let j = 0; (subRule = rule.rules[j]); j++) {
|
|
266
|
-
if (subRule instanceof Node) {
|
|
267
|
-
subRule.copyVisibilityInfo(rule.visibilityInfo());
|
|
268
|
-
if (!(subRule instanceof Declaration) || !subRule.variable) {
|
|
269
|
-
rsRules.splice(++i, 0, subRule);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// Pop the stack
|
|
278
|
-
ctxFrames.shift();
|
|
279
|
-
ctxSelectors.shift();
|
|
280
|
-
|
|
281
|
-
if (context.mediaBlocks) {
|
|
282
|
-
for (i = mediaBlockCount; i < context.mediaBlocks.length; i++) {
|
|
283
|
-
/** @type {RuleNode} */ (context.mediaBlocks[i]).bubbleSelectors(selectors);
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
return ruleset;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/** @param {EvalContext} context */
|
|
291
|
-
evalImports(context) {
|
|
292
|
-
const rules = this.rules;
|
|
293
|
-
/** @type {number} */
|
|
294
|
-
let i;
|
|
295
|
-
/** @type {Node | Node[]} */
|
|
296
|
-
let importRules;
|
|
297
|
-
if (!rules) { return; }
|
|
298
|
-
|
|
299
|
-
for (i = 0; i < rules.length; i++) {
|
|
300
|
-
if (rules[i].type === 'Import') {
|
|
301
|
-
importRules = rules[i].eval(context);
|
|
302
|
-
if (importRules && (/** @type {Node[]} */ (/** @type {unknown} */ (importRules)).length || /** @type {Node[]} */ (/** @type {unknown} */ (importRules)).length === 0)) {
|
|
303
|
-
const importArr = /** @type {Node[]} */ (/** @type {unknown} */ (importRules));
|
|
304
|
-
rules.splice(i, 1, ...importArr);
|
|
305
|
-
i += importArr.length - 1;
|
|
306
|
-
} else {
|
|
307
|
-
rules.splice(i, 1, importRules);
|
|
308
|
-
}
|
|
309
|
-
this.resetCache();
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
makeImportant() {
|
|
315
|
-
const result = new Ruleset(this.selectors, /** @type {Node[]} */ (this.rules).map(function (/** @type {Node & { makeImportant?: () => Node }} */ r) {
|
|
316
|
-
if (r.makeImportant) {
|
|
317
|
-
return r.makeImportant();
|
|
318
|
-
} else {
|
|
319
|
-
return r;
|
|
320
|
-
}
|
|
321
|
-
}), this.strictImports, this.visibilityInfo());
|
|
322
|
-
|
|
323
|
-
return result;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/** @param {Node[] | object[] | null} [args] */
|
|
327
|
-
matchArgs(args) {
|
|
328
|
-
return !args || args.length === 0;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* @param {Node[] | object[] | null} args
|
|
333
|
-
* @param {EvalContext} context
|
|
334
|
-
*/
|
|
335
|
-
matchCondition(args, context) {
|
|
336
|
-
const lastSelector = /** @type {Selector[]} */ (this.selectors)[/** @type {Selector[]} */ (this.selectors).length - 1];
|
|
337
|
-
if (!lastSelector.evaldCondition) {
|
|
338
|
-
return false;
|
|
339
|
-
}
|
|
340
|
-
if (lastSelector.condition &&
|
|
341
|
-
!lastSelector.condition.eval(
|
|
342
|
-
new contexts.Eval(context,
|
|
343
|
-
context.frames))) {
|
|
344
|
-
return false;
|
|
345
|
-
}
|
|
346
|
-
return true;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
resetCache() {
|
|
350
|
-
this._rulesets = null;
|
|
351
|
-
this._variables = null;
|
|
352
|
-
this._properties = null;
|
|
353
|
-
this._lookups = {};
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
variables() {
|
|
357
|
-
if (!this._variables) {
|
|
358
|
-
this._variables = !this.rules ? {} : this.rules.reduce(function (/** @type {Object<string, Declaration>} */ hash, /** @type {Node} */ r) {
|
|
359
|
-
if (r instanceof Declaration && r.variable === true) {
|
|
360
|
-
hash[/** @type {string} */ (r.name)] = r;
|
|
361
|
-
}
|
|
362
|
-
// when evaluating variables in an import statement, imports have not been eval'd
|
|
363
|
-
// so we need to go inside import statements.
|
|
364
|
-
// guard against root being a string (in the case of inlined less)
|
|
365
|
-
if (r.type === 'Import' && /** @type {RuleNode} */ (r).root && /** @type {RuleNode & { root: Ruleset }} */ (r).root.variables) {
|
|
366
|
-
const vars = /** @type {RuleNode & { root: Ruleset }} */ (r).root.variables();
|
|
367
|
-
for (const name in vars) {
|
|
368
|
-
if (Object.prototype.hasOwnProperty.call(vars, name)) {
|
|
369
|
-
hash[name] = /** @type {Declaration} */ (/** @type {RuleNode & { root: Ruleset }} */ (r).root.variable(name));
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
return hash;
|
|
374
|
-
}, {});
|
|
375
|
-
}
|
|
376
|
-
return this._variables;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
properties() {
|
|
380
|
-
if (!this._properties) {
|
|
381
|
-
this._properties = !this.rules ? {} : this.rules.reduce(function (/** @type {Object<string, Declaration[]>} */ hash, /** @type {Node} */ r) {
|
|
382
|
-
if (r instanceof Declaration && r.variable !== true) {
|
|
383
|
-
const name = (/** @type {Node[]} */ (r.name).length === 1) && (/** @type {Node[]} */ (r.name)[0] instanceof Keyword) ?
|
|
384
|
-
/** @type {string} */ (/** @type {Node[]} */ (r.name)[0].value) : /** @type {string} */ (r.name);
|
|
385
|
-
// Properties don't overwrite as they can merge
|
|
386
|
-
if (!hash[`$${name}`]) {
|
|
387
|
-
hash[`$${name}`] = [ r ];
|
|
388
|
-
}
|
|
389
|
-
else {
|
|
390
|
-
hash[`$${name}`].push(r);
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
return hash;
|
|
394
|
-
}, {});
|
|
395
|
-
}
|
|
396
|
-
return this._properties;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
/** @param {string} name */
|
|
400
|
-
variable(name) {
|
|
401
|
-
const decl = this.variables()[name];
|
|
402
|
-
if (decl) {
|
|
403
|
-
return this.parseValue(decl);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
/** @param {string} name */
|
|
408
|
-
property(name) {
|
|
409
|
-
const decl = this.properties()[name];
|
|
410
|
-
if (decl) {
|
|
411
|
-
return this.parseValue(decl);
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
lastDeclaration() {
|
|
416
|
-
for (let i = /** @type {Node[]} */ (this.rules).length; i > 0; i--) {
|
|
417
|
-
const decl = /** @type {Node[]} */ (this.rules)[i - 1];
|
|
418
|
-
if (decl instanceof Declaration) {
|
|
419
|
-
return this.parseValue(decl);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
/** @param {Declaration | Declaration[]} toParse */
|
|
425
|
-
parseValue(toParse) {
|
|
426
|
-
const self = this;
|
|
427
|
-
/** @param {Declaration} decl */
|
|
428
|
-
function transformDeclaration(decl) {
|
|
429
|
-
if (decl.value instanceof Anonymous &&
|
|
430
|
-
!decl.value._preventReparse &&
|
|
431
|
-
!/** @type {Declaration & { parsed?: boolean }} */ (decl).parsed) {
|
|
432
|
-
if (typeof decl.value.value === 'string') {
|
|
433
|
-
new (/** @type {new (...args: [EvalContext, object, FileInfo, number]) => { parseNode: Function }} */ (/** @type {unknown} */ (Parser)))(/** @type {{ context: EvalContext, importManager: object }} */ (/** @type {Ruleset} */ (this).parse).context, /** @type {{ context: EvalContext, importManager: object }} */ (/** @type {Ruleset} */ (this).parse).importManager, decl.fileInfo(), decl.value.getIndex()).parseNode(
|
|
434
|
-
decl.value.value,
|
|
435
|
-
['value', 'important'],
|
|
436
|
-
function(/** @type {Error | null} */ err, /** @type {Node[]} */ result) {
|
|
437
|
-
if (err) {
|
|
438
|
-
decl.parsed = /** @type {Node} */ (/** @type {unknown} */ (true));
|
|
439
|
-
}
|
|
440
|
-
if (result) {
|
|
441
|
-
decl.value = result[0];
|
|
442
|
-
/** @type {Declaration & { important?: string }} */ (decl).important = /** @type {string} */ (/** @type {unknown} */ (result[1])) || '';
|
|
443
|
-
decl.parsed = /** @type {Node} */ (/** @type {unknown} */ (true));
|
|
444
|
-
}
|
|
445
|
-
});
|
|
446
|
-
} else {
|
|
447
|
-
decl.parsed = /** @type {Node} */ (/** @type {unknown} */ (true));
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
return decl;
|
|
451
|
-
}
|
|
452
|
-
else {
|
|
453
|
-
return decl;
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
if (!Array.isArray(toParse)) {
|
|
457
|
-
return transformDeclaration.call(self, toParse);
|
|
458
|
-
}
|
|
459
|
-
else {
|
|
460
|
-
/** @type {Declaration[]} */
|
|
461
|
-
const nodes = [];
|
|
462
|
-
for (let ti = 0; ti < toParse.length; ti++) {
|
|
463
|
-
nodes.push(transformDeclaration.call(self, toParse[ti]));
|
|
464
|
-
}
|
|
465
|
-
return nodes;
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
rulesets() {
|
|
470
|
-
if (!this.rules) { return []; }
|
|
471
|
-
|
|
472
|
-
/** @type {Node[]} */
|
|
473
|
-
const filtRules = [];
|
|
474
|
-
const rules = this.rules;
|
|
475
|
-
/** @type {number} */
|
|
476
|
-
let i;
|
|
477
|
-
/** @type {Node} */
|
|
478
|
-
let rule;
|
|
479
|
-
|
|
480
|
-
for (i = 0; (rule = rules[i]); i++) {
|
|
481
|
-
if (/** @type {RuleNode} */ (rule).isRuleset) {
|
|
482
|
-
filtRules.push(rule);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
return filtRules;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
/** @param {Node} rule */
|
|
490
|
-
prependRule(rule) {
|
|
491
|
-
const rules = this.rules;
|
|
492
|
-
if (rules) {
|
|
493
|
-
rules.unshift(rule);
|
|
494
|
-
} else {
|
|
495
|
-
this.rules = [ rule ];
|
|
496
|
-
}
|
|
497
|
-
this.setParent(rule, this);
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
/**
|
|
501
|
-
* @param {Selector} selector
|
|
502
|
-
* @param {Ruleset | null} [self]
|
|
503
|
-
* @param {((rule: Node) => boolean)} [filter]
|
|
504
|
-
* @returns {{ rule: Node, path: Node[] }[]}
|
|
505
|
-
*/
|
|
506
|
-
find(selector, self, filter) {
|
|
507
|
-
self = self || this;
|
|
508
|
-
/** @type {{ rule: Node, path: Node[] }[]} */
|
|
509
|
-
const rules = [];
|
|
510
|
-
/** @type {number | undefined} */
|
|
511
|
-
let match;
|
|
512
|
-
/** @type {{ rule: Node, path: Node[] }[]} */
|
|
513
|
-
let foundMixins;
|
|
514
|
-
const key = selector.toCSS(/** @type {EvalContext} */ ({}));
|
|
515
|
-
|
|
516
|
-
if (key in this._lookups) { return /** @type {{ rule: Node, path: Node[] }[]} */ (this._lookups[key]); }
|
|
517
|
-
|
|
518
|
-
this.rulesets().forEach(function (rule) {
|
|
519
|
-
if (rule !== self) {
|
|
520
|
-
for (let j = 0; j < /** @type {RuleNode} */ (rule).selectors.length; j++) {
|
|
521
|
-
match = selector.match(/** @type {RuleNode} */ (rule).selectors[j]);
|
|
522
|
-
if (match) {
|
|
523
|
-
if (selector.elements.length > match) {
|
|
524
|
-
if (!filter || filter(rule)) {
|
|
525
|
-
foundMixins = /** @type {Ruleset} */ (/** @type {unknown} */ (rule)).find(new Selector(selector.elements.slice(match)), self, filter);
|
|
526
|
-
for (let i = 0; i < foundMixins.length; ++i) {
|
|
527
|
-
foundMixins[i].path.push(rule);
|
|
528
|
-
}
|
|
529
|
-
Array.prototype.push.apply(rules, foundMixins);
|
|
530
|
-
}
|
|
531
|
-
} else {
|
|
532
|
-
rules.push({ rule, path: []});
|
|
533
|
-
}
|
|
534
|
-
break;
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
});
|
|
539
|
-
this._lookups[key] = rules;
|
|
540
|
-
return rules;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
/**
|
|
544
|
-
* @param {EvalContext} context
|
|
545
|
-
* @param {CSSOutput} output
|
|
546
|
-
*/
|
|
547
|
-
genCSS(context, output) {
|
|
548
|
-
/** @type {number} */
|
|
549
|
-
let i;
|
|
550
|
-
/** @type {number} */
|
|
551
|
-
let j;
|
|
552
|
-
/** @type {Node[]} */
|
|
553
|
-
const charsetRuleNodes = [];
|
|
554
|
-
/** @type {Node[]} */
|
|
555
|
-
let ruleNodes = [];
|
|
556
|
-
|
|
557
|
-
let // Line number debugging
|
|
558
|
-
debugInfo;
|
|
559
|
-
|
|
560
|
-
/** @type {Node} */
|
|
561
|
-
let rule;
|
|
562
|
-
/** @type {Selector[]} */
|
|
563
|
-
let path;
|
|
564
|
-
|
|
565
|
-
context.tabLevel = (context.tabLevel || 0);
|
|
566
|
-
|
|
567
|
-
if (!this.root) {
|
|
568
|
-
context.tabLevel++;
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
const tabRuleStr = context.compress ? '' : Array(context.tabLevel + 1).join(' ');
|
|
572
|
-
const tabSetStr = context.compress ? '' : Array(context.tabLevel).join(' ');
|
|
573
|
-
/** @type {string} */
|
|
574
|
-
let sep;
|
|
575
|
-
|
|
576
|
-
let charsetNodeIndex = 0;
|
|
577
|
-
let importNodeIndex = 0;
|
|
578
|
-
for (i = 0; (rule = /** @type {Node[]} */ (this.rules)[i]); i++) {
|
|
579
|
-
if (rule instanceof Comment) {
|
|
580
|
-
if (importNodeIndex === i) {
|
|
581
|
-
importNodeIndex++;
|
|
582
|
-
}
|
|
583
|
-
ruleNodes.push(rule);
|
|
584
|
-
} else if (/** @type {RuleNode} */ (rule).isCharset && /** @type {RuleNode} */ (rule).isCharset()) {
|
|
585
|
-
ruleNodes.splice(charsetNodeIndex, 0, rule);
|
|
586
|
-
charsetNodeIndex++;
|
|
587
|
-
importNodeIndex++;
|
|
588
|
-
} else if (rule.type === 'Import') {
|
|
589
|
-
ruleNodes.splice(importNodeIndex, 0, rule);
|
|
590
|
-
importNodeIndex++;
|
|
591
|
-
} else {
|
|
592
|
-
ruleNodes.push(rule);
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
ruleNodes = charsetRuleNodes.concat(ruleNodes);
|
|
596
|
-
|
|
597
|
-
// If this is the root node, we don't render
|
|
598
|
-
// a selector, or {}.
|
|
599
|
-
if (!this.root) {
|
|
600
|
-
debugInfo = getDebugInfo(context, /** @type {{ debugInfo: { lineNumber: number, fileName: string } }} */ (/** @type {unknown} */ (this)), tabSetStr);
|
|
601
|
-
|
|
602
|
-
if (debugInfo) {
|
|
603
|
-
output.add(debugInfo);
|
|
604
|
-
output.add(tabSetStr);
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
const paths = /** @type {Selector[][]} */ (this.paths);
|
|
608
|
-
const pathCnt = paths.length;
|
|
609
|
-
/** @type {number} */
|
|
610
|
-
let pathSubCnt;
|
|
611
|
-
|
|
612
|
-
sep = context.compress ? ',' : (`,\n${tabSetStr}`);
|
|
613
|
-
|
|
614
|
-
for (i = 0; i < pathCnt; i++) {
|
|
615
|
-
path = paths[i];
|
|
616
|
-
if (!(pathSubCnt = path.length)) { continue; }
|
|
617
|
-
if (i > 0) { output.add(sep); }
|
|
618
|
-
|
|
619
|
-
/** @type {EvalContext & { firstSelector?: boolean }} */ (context).firstSelector = true;
|
|
620
|
-
path[0].genCSS(context, output);
|
|
621
|
-
|
|
622
|
-
/** @type {EvalContext & { firstSelector?: boolean }} */ (context).firstSelector = false;
|
|
623
|
-
for (j = 1; j < pathSubCnt; j++) {
|
|
624
|
-
path[j].genCSS(context, output);
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
output.add((context.compress ? '{' : ' {\n') + tabRuleStr);
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
// Compile rules and rulesets
|
|
632
|
-
for (i = 0; (rule = ruleNodes[i]); i++) {
|
|
633
|
-
|
|
634
|
-
if (i + 1 === ruleNodes.length) {
|
|
635
|
-
context.lastRule = true;
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
const currentLastRule = context.lastRule;
|
|
639
|
-
if (rule.isRulesetLike()) {
|
|
640
|
-
context.lastRule = false;
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
if (rule.genCSS) {
|
|
644
|
-
rule.genCSS(context, output);
|
|
645
|
-
} else if (rule.value) {
|
|
646
|
-
output.add(/** @type {string} */ (rule.value).toString());
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
context.lastRule = currentLastRule;
|
|
650
|
-
|
|
651
|
-
if (!context.lastRule && rule.isVisible()) {
|
|
652
|
-
output.add(context.compress ? '' : (`\n${tabRuleStr}`));
|
|
653
|
-
} else {
|
|
654
|
-
context.lastRule = false;
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
if (!this.root) {
|
|
659
|
-
output.add((context.compress ? '}' : `\n${tabSetStr}}`));
|
|
660
|
-
context.tabLevel--;
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
if (!output.isEmpty() && !context.compress && this.firstRoot) {
|
|
664
|
-
output.add('\n');
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
/**
|
|
669
|
-
* @param {Selector[][]} paths
|
|
670
|
-
* @param {Selector[][]} context
|
|
671
|
-
* @param {Selector[]} selectors
|
|
672
|
-
*/
|
|
673
|
-
joinSelectors(paths, context, selectors) {
|
|
674
|
-
for (let s = 0; s < selectors.length; s++) {
|
|
675
|
-
this.joinSelector(paths, context, selectors[s]);
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
/**
|
|
680
|
-
* @param {Selector[][]} paths
|
|
681
|
-
* @param {Selector[][]} context
|
|
682
|
-
* @param {Selector} selector
|
|
683
|
-
*/
|
|
684
|
-
joinSelector(paths, context, selector) {
|
|
685
|
-
|
|
686
|
-
/**
|
|
687
|
-
* @param {Selector[]} elementsToPak
|
|
688
|
-
* @param {Element} originalElement
|
|
689
|
-
* @returns {Paren}
|
|
690
|
-
*/
|
|
691
|
-
function createParenthesis(elementsToPak, originalElement) {
|
|
692
|
-
/** @type {Paren} */
|
|
693
|
-
let replacementParen;
|
|
694
|
-
/** @type {number} */
|
|
695
|
-
let j;
|
|
696
|
-
if (elementsToPak.length === 0) {
|
|
697
|
-
replacementParen = new Paren(elementsToPak[0]);
|
|
698
|
-
} else {
|
|
699
|
-
const insideParent = new Array(elementsToPak.length);
|
|
700
|
-
for (j = 0; j < elementsToPak.length; j++) {
|
|
701
|
-
insideParent[j] = new Element(
|
|
702
|
-
null,
|
|
703
|
-
elementsToPak[j],
|
|
704
|
-
originalElement.isVariable,
|
|
705
|
-
originalElement._index,
|
|
706
|
-
originalElement._fileInfo
|
|
707
|
-
);
|
|
708
|
-
}
|
|
709
|
-
replacementParen = new Paren(new Selector(insideParent));
|
|
710
|
-
}
|
|
711
|
-
return replacementParen;
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
/**
|
|
715
|
-
* @param {Paren | Selector} containedElement
|
|
716
|
-
* @param {Element} originalElement
|
|
717
|
-
* @returns {Selector}
|
|
718
|
-
*/
|
|
719
|
-
function createSelector(containedElement, originalElement) {
|
|
720
|
-
/** @type {Element} */
|
|
721
|
-
let element;
|
|
722
|
-
/** @type {Selector} */
|
|
723
|
-
let selector;
|
|
724
|
-
element = new Element(null, containedElement, originalElement.isVariable, originalElement._index, originalElement._fileInfo);
|
|
725
|
-
selector = new Selector([element]);
|
|
726
|
-
return selector;
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
/**
|
|
730
|
-
* @param {Selector[]} beginningPath
|
|
731
|
-
* @param {Selector[]} addPath
|
|
732
|
-
* @param {Element} replacedElement
|
|
733
|
-
* @param {Selector} originalSelector
|
|
734
|
-
* @returns {Selector[]}
|
|
735
|
-
*/
|
|
736
|
-
function addReplacementIntoPath(beginningPath, addPath, replacedElement, originalSelector) {
|
|
737
|
-
/** @type {Selector[]} */
|
|
738
|
-
let newSelectorPath;
|
|
739
|
-
/** @type {Selector} */
|
|
740
|
-
let lastSelector;
|
|
741
|
-
/** @type {Selector} */
|
|
742
|
-
let newJoinedSelector;
|
|
743
|
-
// our new selector path
|
|
744
|
-
newSelectorPath = [];
|
|
745
|
-
|
|
746
|
-
// construct the joined selector - if & is the first thing this will be empty,
|
|
747
|
-
// if not newJoinedSelector will be the last set of elements in the selector
|
|
748
|
-
if (beginningPath.length > 0) {
|
|
749
|
-
newSelectorPath = utils.copyArray(beginningPath);
|
|
750
|
-
lastSelector = newSelectorPath.pop();
|
|
751
|
-
newJoinedSelector = originalSelector.createDerived(utils.copyArray(lastSelector.elements));
|
|
752
|
-
}
|
|
753
|
-
else {
|
|
754
|
-
newJoinedSelector = originalSelector.createDerived([]);
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
if (addPath.length > 0) {
|
|
758
|
-
// /deep/ is a CSS4 selector - (removed, so should deprecate)
|
|
759
|
-
// that is valid without anything in front of it
|
|
760
|
-
// so if the & does not have a combinator that is "" or " " then
|
|
761
|
-
// and there is a combinator on the parent, then grab that.
|
|
762
|
-
// this also allows + a { & .b { .a & { ... though not sure why you would want to do that
|
|
763
|
-
let combinator = replacedElement.combinator;
|
|
764
|
-
|
|
765
|
-
const parentEl = addPath[0].elements[0];
|
|
766
|
-
if (combinator.emptyOrWhitespace && !parentEl.combinator.emptyOrWhitespace) {
|
|
767
|
-
combinator = parentEl.combinator;
|
|
768
|
-
}
|
|
769
|
-
// join the elements so far with the first part of the parent
|
|
770
|
-
newJoinedSelector.elements.push(new Element(
|
|
771
|
-
combinator,
|
|
772
|
-
parentEl.value,
|
|
773
|
-
replacedElement.isVariable,
|
|
774
|
-
replacedElement._index,
|
|
775
|
-
replacedElement._fileInfo
|
|
776
|
-
));
|
|
777
|
-
newJoinedSelector.elements = newJoinedSelector.elements.concat(addPath[0].elements.slice(1));
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
// now add the joined selector - but only if it is not empty
|
|
781
|
-
if (newJoinedSelector.elements.length !== 0) {
|
|
782
|
-
newSelectorPath.push(newJoinedSelector);
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
// put together the parent selectors after the join (e.g. the rest of the parent)
|
|
786
|
-
if (addPath.length > 1) {
|
|
787
|
-
let restOfPath = addPath.slice(1);
|
|
788
|
-
restOfPath = restOfPath.map(function (/** @type {Selector} */ selector) {
|
|
789
|
-
return selector.createDerived(selector.elements, []);
|
|
790
|
-
});
|
|
791
|
-
newSelectorPath = newSelectorPath.concat(restOfPath);
|
|
792
|
-
}
|
|
793
|
-
return newSelectorPath;
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
/**
|
|
797
|
-
* @param {Selector[][]} beginningPath
|
|
798
|
-
* @param {Selector[]} addPaths
|
|
799
|
-
* @param {Element} replacedElement
|
|
800
|
-
* @param {Selector} originalSelector
|
|
801
|
-
* @param {Selector[][]} result
|
|
802
|
-
* @returns {Selector[][]}
|
|
803
|
-
*/
|
|
804
|
-
function addAllReplacementsIntoPath( beginningPath, addPaths, replacedElement, originalSelector, result) {
|
|
805
|
-
/** @type {number} */
|
|
806
|
-
let j;
|
|
807
|
-
for (j = 0; j < beginningPath.length; j++) {
|
|
808
|
-
const newSelectorPath = addReplacementIntoPath(beginningPath[j], addPaths, replacedElement, originalSelector);
|
|
809
|
-
result.push(newSelectorPath);
|
|
810
|
-
}
|
|
811
|
-
return result;
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
/**
|
|
815
|
-
* @param {Element[]} elements
|
|
816
|
-
* @param {Selector[][]} selectors
|
|
817
|
-
*/
|
|
818
|
-
function mergeElementsOnToSelectors(elements, selectors) {
|
|
819
|
-
/** @type {number} */
|
|
820
|
-
let i;
|
|
821
|
-
/** @type {Selector[]} */
|
|
822
|
-
let sel;
|
|
823
|
-
|
|
824
|
-
if (elements.length === 0) {
|
|
825
|
-
return ;
|
|
826
|
-
}
|
|
827
|
-
if (selectors.length === 0) {
|
|
828
|
-
selectors.push([ new Selector(elements) ]);
|
|
829
|
-
return;
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
for (i = 0; (sel = selectors[i]); i++) {
|
|
833
|
-
// if the previous thing in sel is a parent this needs to join on to it
|
|
834
|
-
if (sel.length > 0) {
|
|
835
|
-
sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements));
|
|
836
|
-
}
|
|
837
|
-
else {
|
|
838
|
-
sel.push(new Selector(elements));
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
/**
|
|
844
|
-
* @param {Selector[][]} paths
|
|
845
|
-
* @param {Selector[][]} context
|
|
846
|
-
* @param {Selector} inSelector
|
|
847
|
-
* @returns {boolean}
|
|
848
|
-
*/
|
|
849
|
-
function replaceParentSelector(paths, context, inSelector) {
|
|
850
|
-
// The paths are [[Selector]]
|
|
851
|
-
// The first list is a list of comma separated selectors
|
|
852
|
-
// The inner list is a list of inheritance separated selectors
|
|
853
|
-
// e.g.
|
|
854
|
-
// .a, .b {
|
|
855
|
-
// .c {
|
|
856
|
-
// }
|
|
857
|
-
// }
|
|
858
|
-
// == [[.a] [.c]] [[.b] [.c]]
|
|
859
|
-
//
|
|
860
|
-
/** @type {number} */
|
|
861
|
-
let i;
|
|
862
|
-
/** @type {number} */
|
|
863
|
-
let j;
|
|
864
|
-
/** @type {number} */
|
|
865
|
-
let k;
|
|
866
|
-
/** @type {Element[]} */
|
|
867
|
-
let currentElements;
|
|
868
|
-
/** @type {Selector[][]} */
|
|
869
|
-
let newSelectors;
|
|
870
|
-
/** @type {Selector[][]} */
|
|
871
|
-
let selectorsMultiplied;
|
|
872
|
-
/** @type {Selector[]} */
|
|
873
|
-
let sel;
|
|
874
|
-
/** @type {Element} */
|
|
875
|
-
let el;
|
|
876
|
-
let hadParentSelector = false;
|
|
877
|
-
/** @type {number} */
|
|
878
|
-
let length;
|
|
879
|
-
/** @type {Selector} */
|
|
880
|
-
let lastSelector;
|
|
881
|
-
|
|
882
|
-
/**
|
|
883
|
-
* @param {Element} element
|
|
884
|
-
* @returns {Selector | null}
|
|
885
|
-
*/
|
|
886
|
-
function findNestedSelector(element) {
|
|
887
|
-
/** @type {Node} */
|
|
888
|
-
let maybeSelector;
|
|
889
|
-
if (!(element.value instanceof Paren)) {
|
|
890
|
-
return null;
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
maybeSelector = /** @type {Node} */ (element.value.value);
|
|
894
|
-
if (!(maybeSelector instanceof Selector)) {
|
|
895
|
-
return null;
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
return maybeSelector;
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
// the elements from the current selector so far
|
|
902
|
-
currentElements = [];
|
|
903
|
-
// the current list of new selectors to add to the path.
|
|
904
|
-
// We will build it up. We initiate it with one empty selector as we "multiply" the new selectors
|
|
905
|
-
// by the parents
|
|
906
|
-
newSelectors = [
|
|
907
|
-
[]
|
|
908
|
-
];
|
|
909
|
-
|
|
910
|
-
for (i = 0; (el = inSelector.elements[i]); i++) {
|
|
911
|
-
// non parent reference elements just get added
|
|
912
|
-
if (el.value !== '&') {
|
|
913
|
-
const nestedSelector = findNestedSelector(el);
|
|
914
|
-
if (nestedSelector !== null) {
|
|
915
|
-
// merge the current list of non parent selector elements
|
|
916
|
-
// on to the current list of selectors to add
|
|
917
|
-
mergeElementsOnToSelectors(currentElements, newSelectors);
|
|
918
|
-
|
|
919
|
-
/** @type {Selector[][]} */
|
|
920
|
-
const nestedPaths = [];
|
|
921
|
-
/** @type {boolean | undefined} */
|
|
922
|
-
let replaced;
|
|
923
|
-
/** @type {Selector[][]} */
|
|
924
|
-
const replacedNewSelectors = [];
|
|
925
|
-
|
|
926
|
-
// Check if this is a comma-separated selector list inside the paren
|
|
927
|
-
// e.g. :not(&.a, &.b) produces Selector([Selector, Anonymous(','), Selector])
|
|
928
|
-
const hasSubSelectors = nestedSelector.elements.some(e => e instanceof Selector);
|
|
929
|
-
|
|
930
|
-
if (hasSubSelectors) {
|
|
931
|
-
// Process each sub-selector individually
|
|
932
|
-
/** @type {(Element | Selector)[]} */
|
|
933
|
-
const resolvedElements = [];
|
|
934
|
-
for (const subEl of nestedSelector.elements) {
|
|
935
|
-
if (subEl instanceof Selector) {
|
|
936
|
-
/** @type {Selector[][]} */
|
|
937
|
-
const subPaths = [];
|
|
938
|
-
const subReplaced = replaceParentSelector(subPaths, context, subEl);
|
|
939
|
-
replaced = replaced || subReplaced;
|
|
940
|
-
if (subPaths.length > 0 && subPaths[0].length > 0) {
|
|
941
|
-
resolvedElements.push(subPaths[0][0]);
|
|
942
|
-
} else {
|
|
943
|
-
resolvedElements.push(subEl);
|
|
944
|
-
}
|
|
945
|
-
} else {
|
|
946
|
-
resolvedElements.push(subEl);
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
hadParentSelector = hadParentSelector || /** @type {boolean} */ (replaced);
|
|
950
|
-
const resolvedNestedSelector = new Selector(resolvedElements);
|
|
951
|
-
const replacementSelector = createSelector(createParenthesis([resolvedNestedSelector], el), el);
|
|
952
|
-
addAllReplacementsIntoPath(newSelectors, [replacementSelector], el, inSelector, replacedNewSelectors);
|
|
953
|
-
} else {
|
|
954
|
-
replaced = replaceParentSelector(nestedPaths, context, nestedSelector);
|
|
955
|
-
hadParentSelector = hadParentSelector || replaced;
|
|
956
|
-
// the nestedPaths array should have only one member - replaceParentSelector does not multiply selectors
|
|
957
|
-
for (k = 0; k < nestedPaths.length; k++) {
|
|
958
|
-
const replacementSelector = createSelector(createParenthesis(nestedPaths[k], el), el);
|
|
959
|
-
addAllReplacementsIntoPath(newSelectors, [replacementSelector], el, inSelector, replacedNewSelectors);
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
newSelectors = replacedNewSelectors;
|
|
963
|
-
currentElements = [];
|
|
964
|
-
} else {
|
|
965
|
-
currentElements.push(el);
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
} else {
|
|
969
|
-
hadParentSelector = true;
|
|
970
|
-
// the new list of selectors to add
|
|
971
|
-
selectorsMultiplied = [];
|
|
972
|
-
|
|
973
|
-
// merge the current list of non parent selector elements
|
|
974
|
-
// on to the current list of selectors to add
|
|
975
|
-
mergeElementsOnToSelectors(currentElements, newSelectors);
|
|
976
|
-
|
|
977
|
-
// loop through our current selectors
|
|
978
|
-
for (j = 0; j < newSelectors.length; j++) {
|
|
979
|
-
sel = newSelectors[j];
|
|
980
|
-
// if we don't have any parent paths, the & might be in a mixin so that it can be used
|
|
981
|
-
// whether there are parents or not
|
|
982
|
-
if (context.length === 0) {
|
|
983
|
-
// the combinator used on el should now be applied to the next element instead so that
|
|
984
|
-
// it is not lost
|
|
985
|
-
if (sel.length > 0) {
|
|
986
|
-
sel[0].elements.push(new Element(el.combinator, '', el.isVariable, el._index, el._fileInfo));
|
|
987
|
-
}
|
|
988
|
-
selectorsMultiplied.push(sel);
|
|
989
|
-
}
|
|
990
|
-
else {
|
|
991
|
-
// and the parent selectors
|
|
992
|
-
for (k = 0; k < context.length; k++) {
|
|
993
|
-
// We need to put the current selectors
|
|
994
|
-
// then join the last selector's elements on to the parents selectors
|
|
995
|
-
const newSelectorPath = addReplacementIntoPath(sel, context[k], el, inSelector);
|
|
996
|
-
// add that to our new set of selectors
|
|
997
|
-
selectorsMultiplied.push(newSelectorPath);
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
// our new selectors has been multiplied, so reset the state
|
|
1003
|
-
newSelectors = selectorsMultiplied;
|
|
1004
|
-
currentElements = [];
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
// if we have any elements left over (e.g. .a& .b == .b)
|
|
1009
|
-
// add them on to all the current selectors
|
|
1010
|
-
mergeElementsOnToSelectors(currentElements, newSelectors);
|
|
1011
|
-
|
|
1012
|
-
for (i = 0; i < newSelectors.length; i++) {
|
|
1013
|
-
length = newSelectors[i].length;
|
|
1014
|
-
if (length > 0) {
|
|
1015
|
-
paths.push(newSelectors[i]);
|
|
1016
|
-
lastSelector = newSelectors[i][length - 1];
|
|
1017
|
-
newSelectors[i][length - 1] = lastSelector.createDerived(lastSelector.elements, inSelector.extendList);
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
return hadParentSelector;
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
/**
|
|
1025
|
-
* @param {VisibilityInfo} visibilityInfo
|
|
1026
|
-
* @param {Selector} deriveFrom
|
|
1027
|
-
*/
|
|
1028
|
-
function deriveSelector(visibilityInfo, deriveFrom) {
|
|
1029
|
-
const newSelector = deriveFrom.createDerived(deriveFrom.elements, deriveFrom.extendList, deriveFrom.evaldCondition);
|
|
1030
|
-
newSelector.copyVisibilityInfo(visibilityInfo);
|
|
1031
|
-
return newSelector;
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
// joinSelector code follows
|
|
1035
|
-
/** @type {number} */
|
|
1036
|
-
let i;
|
|
1037
|
-
/** @type {Selector[][]} */
|
|
1038
|
-
let newPaths;
|
|
1039
|
-
/** @type {boolean} */
|
|
1040
|
-
let hadParentSelector;
|
|
1041
|
-
|
|
1042
|
-
newPaths = [];
|
|
1043
|
-
hadParentSelector = replaceParentSelector(newPaths, context, selector);
|
|
1044
|
-
|
|
1045
|
-
if (!hadParentSelector) {
|
|
1046
|
-
if (context.length > 0) {
|
|
1047
|
-
newPaths = [];
|
|
1048
|
-
for (i = 0; i < context.length; i++) {
|
|
1049
|
-
|
|
1050
|
-
const concatenated = context[i].map(deriveSelector.bind(this, selector.visibilityInfo()));
|
|
1051
|
-
|
|
1052
|
-
concatenated.push(selector);
|
|
1053
|
-
newPaths.push(concatenated);
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
else {
|
|
1057
|
-
newPaths = [[selector]];
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
for (i = 0; i < newPaths.length; i++) {
|
|
1062
|
-
paths.push(newPaths[i]);
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
export default Ruleset;
|