@webstudio-is/css-engine 0.145.0 → 0.151.0
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/lib/index.js +260 -113
- package/lib/types/core/atomic.d.ts +12 -0
- package/lib/types/core/create-style-sheet.d.ts +0 -4
- package/lib/types/core/index.d.ts +2 -2
- package/lib/types/core/rules.d.ts +72 -11
- package/lib/types/core/style-sheet-regular.d.ts +1 -3
- package/lib/types/core/style-sheet.d.ts +10 -2
- package/package.json +3 -3
- package/lib/types/core/style-sheet-atomic.d.ts +0 -10
- /package/lib/types/core/{style-sheet-atomic.test.d.ts → atomic.test.d.ts} +0 -0
package/lib/index.js
CHANGED
|
@@ -96,28 +96,141 @@ var toProperty = (property) => {
|
|
|
96
96
|
};
|
|
97
97
|
|
|
98
98
|
// src/core/rules.ts
|
|
99
|
+
var getDeclarationKey = (declaraionKey) => {
|
|
100
|
+
const { breakpoint, selector, property } = declaraionKey;
|
|
101
|
+
return `${breakpoint}:${selector}:${property}`;
|
|
102
|
+
};
|
|
103
|
+
var MixinRule = class {
|
|
104
|
+
// use map to avoid duplicated properties
|
|
105
|
+
#declarations = /* @__PURE__ */ new Map();
|
|
106
|
+
#dirtyBreakpoints = /* @__PURE__ */ new Set();
|
|
107
|
+
/*
|
|
108
|
+
* check if breakpoint was updated
|
|
109
|
+
*/
|
|
110
|
+
isDirtyBreakpoint(breakpoint) {
|
|
111
|
+
return this.#dirtyBreakpoints.has(breakpoint);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* reset breakpoints invalidation
|
|
115
|
+
*/
|
|
116
|
+
clearBreakpoints() {
|
|
117
|
+
this.#dirtyBreakpoints.clear();
|
|
118
|
+
}
|
|
119
|
+
setDeclaration(declaration) {
|
|
120
|
+
this.#declarations.set(getDeclarationKey(declaration), declaration);
|
|
121
|
+
this.#dirtyBreakpoints.add(declaration.breakpoint);
|
|
122
|
+
}
|
|
123
|
+
deleteDeclaration(declaration) {
|
|
124
|
+
this.#declarations.delete(getDeclarationKey(declaration));
|
|
125
|
+
this.#dirtyBreakpoints.add(declaration.breakpoint);
|
|
126
|
+
}
|
|
127
|
+
getDeclarations() {
|
|
128
|
+
return this.#declarations.values();
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
var NestingRule = class {
|
|
132
|
+
#selector;
|
|
133
|
+
#mixinRules = /* @__PURE__ */ new Map();
|
|
134
|
+
#mixins = /* @__PURE__ */ new Set();
|
|
135
|
+
// use map to avoid duplicated properties
|
|
136
|
+
#declarations = /* @__PURE__ */ new Map();
|
|
137
|
+
// cached generated rule by breakpoint
|
|
138
|
+
#cache = /* @__PURE__ */ new Map();
|
|
139
|
+
constructor(selector, mixinRules) {
|
|
140
|
+
this.#selector = selector;
|
|
141
|
+
this.#mixinRules = mixinRules;
|
|
142
|
+
}
|
|
143
|
+
getSelector() {
|
|
144
|
+
return this.#selector;
|
|
145
|
+
}
|
|
146
|
+
addMixin(mixin) {
|
|
147
|
+
this.#mixins.add(mixin);
|
|
148
|
+
this.#cache.clear();
|
|
149
|
+
}
|
|
150
|
+
applyMixins(mixins) {
|
|
151
|
+
this.#mixins = new Set(mixins);
|
|
152
|
+
this.#cache.clear();
|
|
153
|
+
}
|
|
154
|
+
setDeclaration(declaration) {
|
|
155
|
+
this.#declarations.set(getDeclarationKey(declaration), declaration);
|
|
156
|
+
this.#cache.delete(declaration.breakpoint);
|
|
157
|
+
}
|
|
158
|
+
deleteDeclaration(declaration) {
|
|
159
|
+
this.#declarations.delete(getDeclarationKey(declaration));
|
|
160
|
+
this.#cache.delete(declaration.breakpoint);
|
|
161
|
+
}
|
|
162
|
+
getDeclarations() {
|
|
163
|
+
const declarations = /* @__PURE__ */ new Map();
|
|
164
|
+
for (const mixin of this.#mixins) {
|
|
165
|
+
const rule = this.#mixinRules.get(mixin);
|
|
166
|
+
if (rule === void 0) {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
for (const declaration of rule.getDeclarations()) {
|
|
170
|
+
declarations.set(getDeclarationKey(declaration), declaration);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
for (const declaration of this.#declarations.values()) {
|
|
174
|
+
declarations.set(getDeclarationKey(declaration), declaration);
|
|
175
|
+
}
|
|
176
|
+
return declarations.values();
|
|
177
|
+
}
|
|
178
|
+
toString({
|
|
179
|
+
breakpoint,
|
|
180
|
+
indent = 0,
|
|
181
|
+
transformValue
|
|
182
|
+
}) {
|
|
183
|
+
for (const mixin of this.#mixins) {
|
|
184
|
+
const rule = this.#mixinRules.get(mixin);
|
|
185
|
+
if (rule?.isDirtyBreakpoint(breakpoint)) {
|
|
186
|
+
this.#cache.delete(breakpoint);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const cached = this.#cache.get(breakpoint);
|
|
190
|
+
if (cached && cached.indent === indent && cached.transformValue === transformValue) {
|
|
191
|
+
return cached.generated;
|
|
192
|
+
}
|
|
193
|
+
const spaces = " ".repeat(indent);
|
|
194
|
+
const linesBySelector = /* @__PURE__ */ new Map();
|
|
195
|
+
for (const declaration of this.getDeclarations()) {
|
|
196
|
+
if (declaration.breakpoint !== breakpoint) {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
const { selector: nestedSelector, property, value } = declaration;
|
|
200
|
+
const selector = `${this.#selector}${nestedSelector}`;
|
|
201
|
+
const lines = linesBySelector.get(selector) ?? "";
|
|
202
|
+
const propertyString = toProperty(property);
|
|
203
|
+
const valueString = toValue(value, transformValue);
|
|
204
|
+
const line = `${spaces} ${propertyString}: ${valueString}`;
|
|
205
|
+
linesBySelector.set(selector, lines === "" ? line : `${lines};
|
|
206
|
+
${line}`);
|
|
207
|
+
}
|
|
208
|
+
const generated = Array.from(linesBySelector).sort(
|
|
209
|
+
([leftSelector], [rightSelector]) => leftSelector.localeCompare(rightSelector)
|
|
210
|
+
).map(
|
|
211
|
+
([selector, lines]) => `${spaces}${selector} {
|
|
212
|
+
${lines}
|
|
213
|
+
${spaces}}
|
|
214
|
+
`
|
|
215
|
+
).join("").trimEnd();
|
|
216
|
+
this.#cache.set(breakpoint, { generated, indent, transformValue });
|
|
217
|
+
return generated;
|
|
218
|
+
}
|
|
219
|
+
};
|
|
99
220
|
var StylePropertyMap = class {
|
|
221
|
+
#cached;
|
|
100
222
|
#styleMap = /* @__PURE__ */ new Map();
|
|
101
|
-
#isDirty = true;
|
|
102
|
-
#string = "";
|
|
103
223
|
#indent = 0;
|
|
104
224
|
#transformValue;
|
|
105
|
-
|
|
106
|
-
constructor(style, transformValue, onChange) {
|
|
107
|
-
this.#transformValue = transformValue;
|
|
108
|
-
this.#onChange = onChange;
|
|
225
|
+
constructor(style) {
|
|
109
226
|
let property;
|
|
110
227
|
for (property in style) {
|
|
111
228
|
this.#styleMap.set(property, style[property]);
|
|
112
229
|
}
|
|
113
230
|
}
|
|
114
|
-
setTransformer(transformValue) {
|
|
115
|
-
this.#transformValue = transformValue;
|
|
116
|
-
}
|
|
117
231
|
set(property, value) {
|
|
118
232
|
this.#styleMap.set(property, value);
|
|
119
|
-
this.#
|
|
120
|
-
this.#onChange?.();
|
|
233
|
+
this.#cached = void 0;
|
|
121
234
|
}
|
|
122
235
|
get(property) {
|
|
123
236
|
return this.#styleMap.get(property);
|
|
@@ -133,19 +246,19 @@ var StylePropertyMap = class {
|
|
|
133
246
|
}
|
|
134
247
|
delete(property) {
|
|
135
248
|
this.#styleMap.delete(property);
|
|
136
|
-
this.#
|
|
137
|
-
this.#onChange?.();
|
|
249
|
+
this.#cached = void 0;
|
|
138
250
|
}
|
|
139
251
|
clear() {
|
|
140
252
|
this.#styleMap.clear();
|
|
141
|
-
this.#
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
253
|
+
this.#cached = void 0;
|
|
254
|
+
}
|
|
255
|
+
toString({
|
|
256
|
+
indent = 0,
|
|
257
|
+
transformValue
|
|
258
|
+
} = {}) {
|
|
259
|
+
if (this.#cached && indent === this.#indent && transformValue === this.#transformValue) {
|
|
260
|
+
return this.#cached;
|
|
147
261
|
}
|
|
148
|
-
this.#indent = indent;
|
|
149
262
|
const block = [];
|
|
150
263
|
const spaces = " ".repeat(indent);
|
|
151
264
|
for (const [property, value] of this.#styleMap) {
|
|
@@ -153,48 +266,53 @@ var StylePropertyMap = class {
|
|
|
153
266
|
continue;
|
|
154
267
|
}
|
|
155
268
|
block.push(
|
|
156
|
-
`${spaces}${toProperty(property)}: ${toValue(
|
|
157
|
-
value,
|
|
158
|
-
this.#transformValue
|
|
159
|
-
)}`
|
|
269
|
+
`${spaces}${toProperty(property)}: ${toValue(value, transformValue)}`
|
|
160
270
|
);
|
|
161
271
|
}
|
|
162
|
-
this.#
|
|
163
|
-
this.#
|
|
164
|
-
|
|
272
|
+
this.#cached = block.join(";\n");
|
|
273
|
+
this.#indent = indent;
|
|
274
|
+
this.#transformValue = transformValue;
|
|
275
|
+
return this.#cached;
|
|
165
276
|
}
|
|
166
277
|
};
|
|
167
278
|
var StyleRule = class {
|
|
168
279
|
styleMap;
|
|
169
280
|
selectorText;
|
|
170
|
-
constructor(selectorText, style
|
|
281
|
+
constructor(selectorText, style) {
|
|
171
282
|
this.selectorText = selectorText;
|
|
172
|
-
this.styleMap = style instanceof StylePropertyMap ? style : new StylePropertyMap(style
|
|
283
|
+
this.styleMap = style instanceof StylePropertyMap ? style : new StylePropertyMap(style);
|
|
173
284
|
}
|
|
174
285
|
get cssText() {
|
|
175
286
|
return this.toString();
|
|
176
287
|
}
|
|
177
|
-
toString(
|
|
178
|
-
|
|
288
|
+
toString({
|
|
289
|
+
indent = 0,
|
|
290
|
+
transformValue
|
|
291
|
+
} = {}) {
|
|
292
|
+
const spaces = " ".repeat(indent);
|
|
293
|
+
const content = this.styleMap.toString({
|
|
294
|
+
indent: indent + 2,
|
|
295
|
+
transformValue
|
|
296
|
+
});
|
|
179
297
|
return `${spaces}${this.selectorText} {
|
|
180
|
-
${
|
|
181
|
-
indent: options.indent + 2
|
|
182
|
-
})}
|
|
298
|
+
${content}
|
|
183
299
|
${spaces}}`;
|
|
184
300
|
}
|
|
185
301
|
};
|
|
186
302
|
var MediaRule = class {
|
|
303
|
+
#name;
|
|
187
304
|
options;
|
|
188
305
|
rules;
|
|
189
306
|
#mediaType;
|
|
190
|
-
constructor(options = {}) {
|
|
307
|
+
constructor(name, options = {}) {
|
|
308
|
+
this.#name = name;
|
|
191
309
|
this.options = options;
|
|
192
310
|
this.rules = /* @__PURE__ */ new Map();
|
|
193
311
|
this.#mediaType = options.mediaType ?? "all";
|
|
194
312
|
}
|
|
195
313
|
insertRule(rule) {
|
|
196
314
|
this.rules.set(
|
|
197
|
-
|
|
315
|
+
rule instanceof StyleRule ? rule.selectorText : rule.cssText,
|
|
198
316
|
rule
|
|
199
317
|
);
|
|
200
318
|
return rule;
|
|
@@ -203,12 +321,31 @@ var MediaRule = class {
|
|
|
203
321
|
return this.toString();
|
|
204
322
|
}
|
|
205
323
|
toString() {
|
|
206
|
-
|
|
324
|
+
return this.generateRule({ nestingRules: [] });
|
|
325
|
+
}
|
|
326
|
+
generateRule({
|
|
327
|
+
nestingRules,
|
|
328
|
+
transformValue
|
|
329
|
+
}) {
|
|
330
|
+
if (this.rules.size === 0 && nestingRules.length === 0) {
|
|
207
331
|
return "";
|
|
208
332
|
}
|
|
209
333
|
const rules = [];
|
|
210
334
|
for (const rule of this.rules.values()) {
|
|
211
|
-
rules.push(rule.toString({ indent: 2 }));
|
|
335
|
+
rules.push(rule.toString({ indent: 2, transformValue }));
|
|
336
|
+
}
|
|
337
|
+
for (const rule of nestingRules) {
|
|
338
|
+
const generatedRule = rule.toString({
|
|
339
|
+
breakpoint: this.#name,
|
|
340
|
+
indent: 2,
|
|
341
|
+
transformValue
|
|
342
|
+
});
|
|
343
|
+
if (generatedRule !== "") {
|
|
344
|
+
rules.push(generatedRule);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if (rules.length === 0) {
|
|
348
|
+
return "";
|
|
212
349
|
}
|
|
213
350
|
let conditionText = "";
|
|
214
351
|
const { minWidth, maxWidth } = this.options;
|
|
@@ -227,26 +364,28 @@ ${rules.join(
|
|
|
227
364
|
};
|
|
228
365
|
var PlaintextRule = class {
|
|
229
366
|
cssText;
|
|
230
|
-
styleMap;
|
|
231
367
|
constructor(cssText) {
|
|
232
368
|
this.cssText = cssText;
|
|
233
|
-
this.styleMap = new StylePropertyMap({});
|
|
234
369
|
}
|
|
235
370
|
toString() {
|
|
236
371
|
return this.cssText;
|
|
237
372
|
}
|
|
238
373
|
};
|
|
239
374
|
var FontFaceRule = class {
|
|
240
|
-
|
|
375
|
+
#cached;
|
|
376
|
+
#options;
|
|
241
377
|
constructor(options) {
|
|
242
|
-
this
|
|
378
|
+
this.#options = options;
|
|
243
379
|
}
|
|
244
380
|
get cssText() {
|
|
245
381
|
return this.toString();
|
|
246
382
|
}
|
|
247
383
|
toString() {
|
|
384
|
+
if (this.#cached) {
|
|
385
|
+
return this.#cached;
|
|
386
|
+
}
|
|
248
387
|
const decls = [];
|
|
249
|
-
const { fontFamily, fontStyle, fontWeight, fontDisplay, src } = this
|
|
388
|
+
const { fontFamily, fontStyle, fontWeight, fontDisplay, src } = this.#options;
|
|
250
389
|
const value = toValue(
|
|
251
390
|
{ type: "fontFamily", value: [fontFamily] },
|
|
252
391
|
// Avoids adding a fallback automatically which needs to happen for font family in general but not for font face.
|
|
@@ -257,9 +396,10 @@ var FontFaceRule = class {
|
|
|
257
396
|
decls.push(`font-weight: ${fontWeight}`);
|
|
258
397
|
decls.push(`font-display: ${fontDisplay}`);
|
|
259
398
|
decls.push(`src: ${src}`);
|
|
260
|
-
|
|
399
|
+
this.#cached = `@font-face {
|
|
261
400
|
${decls.join("; ")};
|
|
262
401
|
}`;
|
|
402
|
+
return this.#cached;
|
|
263
403
|
}
|
|
264
404
|
};
|
|
265
405
|
|
|
@@ -325,23 +465,26 @@ var StyleSheet = class {
|
|
|
325
465
|
#cssText = "";
|
|
326
466
|
#mediaRules = /* @__PURE__ */ new Map();
|
|
327
467
|
#plainRules = /* @__PURE__ */ new Map();
|
|
468
|
+
#mixinRules = /* @__PURE__ */ new Map();
|
|
469
|
+
nestingRules = /* @__PURE__ */ new Map();
|
|
328
470
|
#fontFaceRules = [];
|
|
329
|
-
#
|
|
471
|
+
#transformValue;
|
|
330
472
|
#element;
|
|
331
473
|
constructor(element) {
|
|
332
474
|
this.#element = element;
|
|
333
475
|
}
|
|
476
|
+
setTransformer(transformValue) {
|
|
477
|
+
this.#transformValue = transformValue;
|
|
478
|
+
}
|
|
334
479
|
addMediaRule(id, options) {
|
|
335
480
|
let mediaRule = this.#mediaRules.get(id);
|
|
336
481
|
if (mediaRule === void 0) {
|
|
337
|
-
mediaRule = new MediaRule(options);
|
|
482
|
+
mediaRule = new MediaRule(id, options);
|
|
338
483
|
this.#mediaRules.set(id, mediaRule);
|
|
339
|
-
this.#isDirty = true;
|
|
340
484
|
return mediaRule;
|
|
341
485
|
}
|
|
342
486
|
if (options) {
|
|
343
487
|
mediaRule.options = options;
|
|
344
|
-
this.#isDirty = true;
|
|
345
488
|
}
|
|
346
489
|
if (mediaRule === void 0) {
|
|
347
490
|
throw new Error("No media rule found");
|
|
@@ -353,21 +496,31 @@ var StyleSheet = class {
|
|
|
353
496
|
if (rule !== void 0) {
|
|
354
497
|
return rule;
|
|
355
498
|
}
|
|
356
|
-
this.#isDirty = true;
|
|
357
499
|
return this.#plainRules.set(cssText, new PlaintextRule(cssText));
|
|
358
500
|
}
|
|
501
|
+
addMixinRule(name) {
|
|
502
|
+
let rule = this.#mixinRules.get(name);
|
|
503
|
+
if (rule === void 0) {
|
|
504
|
+
rule = new MixinRule();
|
|
505
|
+
this.#mixinRules.set(name, rule);
|
|
506
|
+
}
|
|
507
|
+
return rule;
|
|
508
|
+
}
|
|
509
|
+
addNestingRule(selector) {
|
|
510
|
+
let rule = this.nestingRules.get(selector);
|
|
511
|
+
if (rule === void 0) {
|
|
512
|
+
rule = new NestingRule(selector, this.#mixinRules);
|
|
513
|
+
this.nestingRules.set(selector, rule);
|
|
514
|
+
}
|
|
515
|
+
return rule;
|
|
516
|
+
}
|
|
359
517
|
addFontFaceRule(options) {
|
|
360
|
-
this.#isDirty = true;
|
|
361
518
|
return this.#fontFaceRules.push(new FontFaceRule(options));
|
|
362
519
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
if (this.#isDirty === false) {
|
|
368
|
-
return this.#cssText;
|
|
369
|
-
}
|
|
370
|
-
this.#isDirty = false;
|
|
520
|
+
generateWith({
|
|
521
|
+
nestingRules,
|
|
522
|
+
transformValue
|
|
523
|
+
}) {
|
|
371
524
|
const css = [];
|
|
372
525
|
css.push(...this.#fontFaceRules.map((rule) => rule.cssText));
|
|
373
526
|
for (const plaintextRule of this.#plainRules.values()) {
|
|
@@ -377,19 +530,32 @@ var StyleSheet = class {
|
|
|
377
530
|
(ruleA, ruleB) => compareMedia(ruleA.options, ruleB.options)
|
|
378
531
|
);
|
|
379
532
|
for (const mediaRule of sortedMediaRules) {
|
|
380
|
-
const
|
|
533
|
+
const cssText = mediaRule.generateRule({
|
|
534
|
+
nestingRules,
|
|
535
|
+
transformValue
|
|
536
|
+
});
|
|
381
537
|
if (cssText !== "") {
|
|
382
538
|
css.push(cssText);
|
|
383
539
|
}
|
|
384
540
|
}
|
|
541
|
+
for (const rule of this.#mixinRules.values()) {
|
|
542
|
+
rule.clearBreakpoints();
|
|
543
|
+
}
|
|
385
544
|
this.#cssText = css.join("\n");
|
|
386
545
|
return this.#cssText;
|
|
387
546
|
}
|
|
547
|
+
get cssText() {
|
|
548
|
+
return this.generateWith({
|
|
549
|
+
nestingRules: Array.from(this.nestingRules.values()),
|
|
550
|
+
transformValue: this.#transformValue
|
|
551
|
+
});
|
|
552
|
+
}
|
|
388
553
|
clear() {
|
|
389
554
|
this.#mediaRules.clear();
|
|
555
|
+
this.#mixinRules.clear();
|
|
556
|
+
this.nestingRules.clear();
|
|
390
557
|
this.#plainRules.clear();
|
|
391
558
|
this.#fontFaceRules = [];
|
|
392
|
-
this.#isDirty = true;
|
|
393
559
|
}
|
|
394
560
|
render() {
|
|
395
561
|
this.#element.mount();
|
|
@@ -409,58 +575,12 @@ var StyleSheet = class {
|
|
|
409
575
|
// src/core/style-sheet-regular.ts
|
|
410
576
|
var defaultMediaRuleId = "__default-media-rule__";
|
|
411
577
|
var StyleSheetRegular = class extends StyleSheet {
|
|
412
|
-
addStyleRule(rule, selectorText
|
|
578
|
+
addStyleRule(rule, selectorText) {
|
|
413
579
|
const mediaRule = this.addMediaRule(rule.breakpoint || defaultMediaRuleId);
|
|
414
|
-
|
|
415
|
-
const styleRule = new StyleRule(
|
|
416
|
-
selectorText,
|
|
417
|
-
rule.style,
|
|
418
|
-
transformValue,
|
|
419
|
-
this.#onChangeRule
|
|
420
|
-
);
|
|
580
|
+
const styleRule = new StyleRule(selectorText, rule.style);
|
|
421
581
|
mediaRule.insertRule(styleRule);
|
|
422
582
|
return styleRule;
|
|
423
583
|
}
|
|
424
|
-
#onChangeRule = () => {
|
|
425
|
-
this.markAsDirty();
|
|
426
|
-
};
|
|
427
|
-
};
|
|
428
|
-
|
|
429
|
-
// src/core/style-sheet-atomic.ts
|
|
430
|
-
import hash from "@emotion/hash";
|
|
431
|
-
var defaultMediaRuleId2 = "__default-media-rule__";
|
|
432
|
-
var StyleSheetAtomic = class extends StyleSheet {
|
|
433
|
-
addStyleRule({ style, breakpoint }, selectorSuffix = "", transformValue) {
|
|
434
|
-
const mediaRule = this.addMediaRule(breakpoint || defaultMediaRuleId2);
|
|
435
|
-
const styleRules = [];
|
|
436
|
-
const classes = [];
|
|
437
|
-
let property;
|
|
438
|
-
for (property in style) {
|
|
439
|
-
const stylePropertyMap = new StylePropertyMap(
|
|
440
|
-
{ [property]: style[property] },
|
|
441
|
-
transformValue
|
|
442
|
-
);
|
|
443
|
-
const className = `c${hash(
|
|
444
|
-
stylePropertyMap + selectorSuffix + breakpoint
|
|
445
|
-
)}`;
|
|
446
|
-
classes.push(className);
|
|
447
|
-
const newStyleRule = new StyleRule(
|
|
448
|
-
`.${className}${selectorSuffix}`,
|
|
449
|
-
stylePropertyMap,
|
|
450
|
-
transformValue,
|
|
451
|
-
this.#onChangeRule
|
|
452
|
-
);
|
|
453
|
-
styleRules.push(newStyleRule);
|
|
454
|
-
if (mediaRule.rules.has(newStyleRule.selectorText) === false) {
|
|
455
|
-
mediaRule.insertRule(newStyleRule);
|
|
456
|
-
this.markAsDirty();
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
return { styleRules, classes };
|
|
460
|
-
}
|
|
461
|
-
#onChangeRule = () => {
|
|
462
|
-
this.markAsDirty();
|
|
463
|
-
};
|
|
464
584
|
};
|
|
465
585
|
|
|
466
586
|
// src/core/create-style-sheet.ts
|
|
@@ -468,10 +588,6 @@ var createRegularStyleSheet = (options) => {
|
|
|
468
588
|
const element = new StyleElement(options?.name);
|
|
469
589
|
return new StyleSheetRegular(element);
|
|
470
590
|
};
|
|
471
|
-
var createAtomicStyleSheet = (options) => {
|
|
472
|
-
const element = new StyleElement(options?.name);
|
|
473
|
-
return new StyleSheetAtomic(element);
|
|
474
|
-
};
|
|
475
591
|
|
|
476
592
|
// src/core/match-media.ts
|
|
477
593
|
var matchMedia = (options, width) => {
|
|
@@ -495,6 +611,37 @@ var findApplicableMedia = (media, width) => {
|
|
|
495
611
|
}
|
|
496
612
|
};
|
|
497
613
|
|
|
614
|
+
// src/core/atomic.ts
|
|
615
|
+
import hash from "@emotion/hash";
|
|
616
|
+
var generateAtomic = (sheet, options) => {
|
|
617
|
+
const { getKey, transformValue } = options;
|
|
618
|
+
const atomicRules = /* @__PURE__ */ new Map();
|
|
619
|
+
const classesMap = /* @__PURE__ */ new Map();
|
|
620
|
+
for (const rule of sheet.nestingRules.values()) {
|
|
621
|
+
const groupKey = getKey(rule);
|
|
622
|
+
const classList = [];
|
|
623
|
+
for (const declaration of rule.getDeclarations()) {
|
|
624
|
+
const atomicHash = hash(
|
|
625
|
+
declaration.breakpoint + declaration.selector + declaration.property + toValue(declaration.value, transformValue)
|
|
626
|
+
);
|
|
627
|
+
const className = `c${atomicHash}`;
|
|
628
|
+
let atomicRule = atomicRules.get(atomicHash);
|
|
629
|
+
if (atomicRule === void 0) {
|
|
630
|
+
atomicRule = new NestingRule(`.${className}`, /* @__PURE__ */ new Map());
|
|
631
|
+
atomicRule.setDeclaration(declaration);
|
|
632
|
+
atomicRules.set(atomicHash, atomicRule);
|
|
633
|
+
}
|
|
634
|
+
classList.push(className);
|
|
635
|
+
}
|
|
636
|
+
classesMap.set(groupKey, classList);
|
|
637
|
+
}
|
|
638
|
+
const cssText = sheet.generateWith({
|
|
639
|
+
nestingRules: Array.from(atomicRules.values()),
|
|
640
|
+
transformValue
|
|
641
|
+
});
|
|
642
|
+
return { cssText, classesMap };
|
|
643
|
+
};
|
|
644
|
+
|
|
498
645
|
// src/schema.ts
|
|
499
646
|
import { z } from "zod";
|
|
500
647
|
var Unit = z.string();
|
|
@@ -618,10 +765,10 @@ export {
|
|
|
618
765
|
UnitValue,
|
|
619
766
|
UnparsedValue,
|
|
620
767
|
compareMedia,
|
|
621
|
-
createAtomicStyleSheet,
|
|
622
768
|
createRegularStyleSheet,
|
|
623
769
|
equalMedia,
|
|
624
770
|
findApplicableMedia,
|
|
771
|
+
generateAtomic,
|
|
625
772
|
isValidStaticStyleValue,
|
|
626
773
|
matchMedia,
|
|
627
774
|
toProperty,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { StyleSheet } from "./style-sheet";
|
|
2
|
+
import { NestingRule } from "./rules";
|
|
3
|
+
import { type TransformValue } from "./to-value";
|
|
4
|
+
type Options = {
|
|
5
|
+
getKey: (rule: NestingRule) => string;
|
|
6
|
+
transformValue?: TransformValue;
|
|
7
|
+
};
|
|
8
|
+
export declare const generateAtomic: (sheet: StyleSheet, options: Options) => {
|
|
9
|
+
cssText: string;
|
|
10
|
+
classesMap: Map<string, string[]>;
|
|
11
|
+
};
|
|
12
|
+
export {};
|
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
import { StyleSheetRegular } from "./style-sheet-regular";
|
|
2
|
-
import { StyleSheetAtomic } from "./style-sheet-atomic";
|
|
3
2
|
export declare const createRegularStyleSheet: (options?: {
|
|
4
3
|
name?: string;
|
|
5
4
|
}) => StyleSheetRegular;
|
|
6
|
-
export declare const createAtomicStyleSheet: (options?: {
|
|
7
|
-
name?: string;
|
|
8
|
-
}) => StyleSheetAtomic;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
export type {
|
|
1
|
+
export type { NestingRule, StyleRule, MediaRule, PlaintextRule, FontFaceRule, } from "./rules";
|
|
2
2
|
export type { StyleSheetRegular } from "./style-sheet-regular";
|
|
3
|
-
export type { StyleSheetAtomic } from "./style-sheet-atomic";
|
|
4
3
|
export * from "./create-style-sheet";
|
|
5
4
|
export * from "./to-value";
|
|
6
5
|
export * from "./to-property";
|
|
@@ -8,3 +7,4 @@ export * from "./match-media";
|
|
|
8
7
|
export * from "./equal-media";
|
|
9
8
|
export * from "./compare-media";
|
|
10
9
|
export * from "./find-applicable-media";
|
|
10
|
+
export * from "./atomic";
|
|
@@ -1,9 +1,65 @@
|
|
|
1
1
|
import type { Style, StyleProperty, StyleValue } from "../schema";
|
|
2
2
|
import { type TransformValue } from "./to-value";
|
|
3
|
+
type Declaration = {
|
|
4
|
+
breakpoint: string;
|
|
5
|
+
selector: string;
|
|
6
|
+
property: StyleProperty;
|
|
7
|
+
value: StyleValue;
|
|
8
|
+
};
|
|
9
|
+
type DeclarationKey = Omit<Declaration, "value">;
|
|
10
|
+
/**
|
|
11
|
+
* Reusable style rule in any nesting rule
|
|
12
|
+
*
|
|
13
|
+
* @mixin name {
|
|
14
|
+
* \@media breakpoint {
|
|
15
|
+
* &selector {
|
|
16
|
+
* property: value
|
|
17
|
+
* }
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
*/
|
|
21
|
+
export declare class MixinRule {
|
|
22
|
+
#private;
|
|
23
|
+
isDirtyBreakpoint(breakpoint: string): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* reset breakpoints invalidation
|
|
26
|
+
*/
|
|
27
|
+
clearBreakpoints(): void;
|
|
28
|
+
setDeclaration(declaration: Declaration): void;
|
|
29
|
+
deleteDeclaration(declaration: DeclarationKey): void;
|
|
30
|
+
getDeclarations(): IterableIterator<Declaration>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Universal style rule with nested selectors and media queries support
|
|
34
|
+
* Rules are generated by each media query
|
|
35
|
+
* and heavily cached to avoid complex computation
|
|
36
|
+
*
|
|
37
|
+
* selector {
|
|
38
|
+
* \@media breakpoint {
|
|
39
|
+
* &selector {
|
|
40
|
+
* property: value
|
|
41
|
+
* }
|
|
42
|
+
* }
|
|
43
|
+
* }
|
|
44
|
+
*/
|
|
45
|
+
export declare class NestingRule {
|
|
46
|
+
#private;
|
|
47
|
+
constructor(selector: string, mixinRules: Map<string, MixinRule>);
|
|
48
|
+
getSelector(): string;
|
|
49
|
+
addMixin(mixin: string): void;
|
|
50
|
+
applyMixins(mixins: string[]): void;
|
|
51
|
+
setDeclaration(declaration: Declaration): void;
|
|
52
|
+
deleteDeclaration(declaration: DeclarationKey): void;
|
|
53
|
+
getDeclarations(): IterableIterator<Declaration>;
|
|
54
|
+
toString({ breakpoint, indent, transformValue, }: {
|
|
55
|
+
breakpoint: string;
|
|
56
|
+
indent?: number;
|
|
57
|
+
transformValue?: TransformValue;
|
|
58
|
+
}): string;
|
|
59
|
+
}
|
|
3
60
|
export declare class StylePropertyMap {
|
|
4
61
|
#private;
|
|
5
|
-
constructor(style: Style
|
|
6
|
-
setTransformer(transformValue: TransformValue): void;
|
|
62
|
+
constructor(style: Style);
|
|
7
63
|
set(property: StyleProperty, value?: StyleValue): void;
|
|
8
64
|
get(property: StyleProperty): {
|
|
9
65
|
type: "unit";
|
|
@@ -1024,17 +1080,19 @@ export declare class StylePropertyMap {
|
|
|
1024
1080
|
keys(): IterableIterator<StyleProperty>;
|
|
1025
1081
|
delete(property: StyleProperty): void;
|
|
1026
1082
|
clear(): void;
|
|
1027
|
-
toString({ indent }?: {
|
|
1028
|
-
indent?: number
|
|
1083
|
+
toString({ indent, transformValue, }?: {
|
|
1084
|
+
indent?: number;
|
|
1085
|
+
transformValue?: TransformValue;
|
|
1029
1086
|
}): string;
|
|
1030
1087
|
}
|
|
1031
1088
|
export declare class StyleRule {
|
|
1032
1089
|
styleMap: StylePropertyMap;
|
|
1033
1090
|
selectorText: string;
|
|
1034
|
-
constructor(selectorText: string, style: StylePropertyMap | Style
|
|
1091
|
+
constructor(selectorText: string, style: StylePropertyMap | Style);
|
|
1035
1092
|
get cssText(): string;
|
|
1036
|
-
toString(
|
|
1037
|
-
indent
|
|
1093
|
+
toString({ indent, transformValue, }?: {
|
|
1094
|
+
indent?: number;
|
|
1095
|
+
transformValue?: TransformValue;
|
|
1038
1096
|
}): string;
|
|
1039
1097
|
}
|
|
1040
1098
|
export type MediaRuleOptions = {
|
|
@@ -1046,14 +1104,17 @@ export declare class MediaRule {
|
|
|
1046
1104
|
#private;
|
|
1047
1105
|
options: MediaRuleOptions;
|
|
1048
1106
|
rules: Map<string, StyleRule | PlaintextRule>;
|
|
1049
|
-
constructor(options?: MediaRuleOptions);
|
|
1107
|
+
constructor(name: string, options?: MediaRuleOptions);
|
|
1050
1108
|
insertRule(rule: StyleRule | PlaintextRule): StyleRule | PlaintextRule;
|
|
1051
1109
|
get cssText(): string;
|
|
1052
1110
|
toString(): string;
|
|
1111
|
+
generateRule({ nestingRules, transformValue, }: {
|
|
1112
|
+
nestingRules: NestingRule[];
|
|
1113
|
+
transformValue?: TransformValue;
|
|
1114
|
+
}): string;
|
|
1053
1115
|
}
|
|
1054
1116
|
export declare class PlaintextRule {
|
|
1055
1117
|
cssText: string;
|
|
1056
|
-
styleMap: StylePropertyMap;
|
|
1057
1118
|
constructor(cssText: string);
|
|
1058
1119
|
toString(): string;
|
|
1059
1120
|
}
|
|
@@ -1065,9 +1126,9 @@ export type FontFaceOptions = {
|
|
|
1065
1126
|
src: string;
|
|
1066
1127
|
};
|
|
1067
1128
|
export declare class FontFaceRule {
|
|
1068
|
-
|
|
1129
|
+
#private;
|
|
1069
1130
|
constructor(options: FontFaceOptions);
|
|
1070
1131
|
get cssText(): string;
|
|
1071
1132
|
toString(): string;
|
|
1072
1133
|
}
|
|
1073
|
-
export
|
|
1134
|
+
export {};
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { StyleRule } from "./rules";
|
|
2
|
-
import type { TransformValue } from "./to-value";
|
|
3
2
|
import { StyleSheet, type CssRule } from "./style-sheet";
|
|
4
3
|
export declare class StyleSheetRegular extends StyleSheet {
|
|
5
|
-
|
|
6
|
-
addStyleRule(rule: CssRule, selectorText: string, transformValue?: TransformValue): StyleRule;
|
|
4
|
+
addStyleRule(rule: CssRule, selectorText: string): StyleRule;
|
|
7
5
|
}
|
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import type { Style } from "../schema";
|
|
2
|
-
import { MediaRule, PlaintextRule, type FontFaceOptions, type MediaRuleOptions } from "./rules";
|
|
2
|
+
import { MediaRule, MixinRule, NestingRule, PlaintextRule, type FontFaceOptions, type MediaRuleOptions } from "./rules";
|
|
3
3
|
import { StyleElement } from "./style-element";
|
|
4
|
+
import type { TransformValue } from "./to-value";
|
|
4
5
|
export type CssRule = {
|
|
5
6
|
style: Style;
|
|
6
7
|
breakpoint?: string;
|
|
7
8
|
};
|
|
8
9
|
export declare class StyleSheet {
|
|
9
10
|
#private;
|
|
11
|
+
nestingRules: Map<string, NestingRule>;
|
|
10
12
|
constructor(element: StyleElement);
|
|
13
|
+
setTransformer(transformValue: TransformValue): void;
|
|
11
14
|
addMediaRule(id: string, options?: MediaRuleOptions): MediaRule;
|
|
12
15
|
addPlaintextRule(cssText: string): PlaintextRule | Map<string, PlaintextRule>;
|
|
16
|
+
addMixinRule(name: string): MixinRule;
|
|
17
|
+
addNestingRule(selector: string): NestingRule;
|
|
13
18
|
addFontFaceRule(options: FontFaceOptions): number;
|
|
14
|
-
|
|
19
|
+
generateWith({ nestingRules, transformValue, }: {
|
|
20
|
+
nestingRules: NestingRule[];
|
|
21
|
+
transformValue?: TransformValue;
|
|
22
|
+
}): string;
|
|
15
23
|
get cssText(): string;
|
|
16
24
|
clear(): void;
|
|
17
25
|
render(): void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webstudio-is/css-engine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.151.0",
|
|
4
4
|
"description": "CSS Renderer for Webstudio",
|
|
5
5
|
"author": "Webstudio <github@webstudio.is>",
|
|
6
6
|
"homepage": "https://webstudio.is",
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"@emotion/hash": "^0.9.1",
|
|
10
10
|
"hyphenate-style-name": "^1.0.4",
|
|
11
11
|
"zod": "^3.22.4",
|
|
12
|
-
"@webstudio-is/fonts": "0.
|
|
13
|
-
"@webstudio-is/error-utils": "0.
|
|
12
|
+
"@webstudio-is/fonts": "0.151.0",
|
|
13
|
+
"@webstudio-is/error-utils": "0.151.0"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@jest/globals": "^29.7.0",
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { StyleRule } from "./rules";
|
|
2
|
-
import type { TransformValue } from "./to-value";
|
|
3
|
-
import { StyleSheet, type CssRule } from "./style-sheet";
|
|
4
|
-
export declare class StyleSheetAtomic extends StyleSheet {
|
|
5
|
-
#private;
|
|
6
|
-
addStyleRule({ style, breakpoint }: CssRule, selectorSuffix?: string, transformValue?: TransformValue): {
|
|
7
|
-
styleRules: StyleRule[];
|
|
8
|
-
classes: string[];
|
|
9
|
-
};
|
|
10
|
-
}
|
|
File without changes
|