@webstudio-is/css-engine 0.72.0 → 0.74.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.
@@ -44,12 +44,13 @@ __export(rules_exports, {
44
44
  module.exports = __toCommonJS(rules_exports);
45
45
  var import_to_value = require("./to-value");
46
46
  var import_to_property = require("./to-property");
47
- var _styleMap, _isDirty, _string, _transformValue, _onChange, _mediaType;
47
+ var _styleMap, _isDirty, _string, _indent, _transformValue, _onChange, _mediaType;
48
48
  class StylePropertyMap {
49
49
  constructor(transformValue) {
50
50
  __privateAdd(this, _styleMap, /* @__PURE__ */ new Map());
51
51
  __privateAdd(this, _isDirty, false);
52
52
  __privateAdd(this, _string, "");
53
+ __privateAdd(this, _indent, 0);
53
54
  __privateAdd(this, _transformValue, void 0);
54
55
  __privateSet(this, _transformValue, transformValue);
55
56
  }
@@ -77,20 +78,25 @@ class StylePropertyMap {
77
78
  __privateSet(this, _isDirty, true);
78
79
  this.onChange?.();
79
80
  }
80
- toString() {
81
- if (__privateGet(this, _isDirty) === false) {
81
+ toString({ indent = 0 } = {}) {
82
+ if (__privateGet(this, _isDirty) === false && indent === __privateGet(this, _indent)) {
82
83
  return __privateGet(this, _string);
83
84
  }
85
+ __privateSet(this, _indent, indent);
84
86
  const block = [];
87
+ const spaces = " ".repeat(indent);
85
88
  for (const [property, value] of __privateGet(this, _styleMap)) {
86
89
  if (value === void 0) {
87
90
  continue;
88
91
  }
89
92
  block.push(
90
- `${(0, import_to_property.toProperty)(property)}: ${(0, import_to_value.toValue)(value, __privateGet(this, _transformValue))}`
93
+ `${spaces}${(0, import_to_property.toProperty)(property)}: ${(0, import_to_value.toValue)(
94
+ value,
95
+ __privateGet(this, _transformValue)
96
+ )}`
91
97
  );
92
98
  }
93
- __privateSet(this, _string, block.join("; "));
99
+ __privateSet(this, _string, block.join(";\n"));
94
100
  __privateSet(this, _isDirty, false);
95
101
  return __privateGet(this, _string);
96
102
  }
@@ -98,6 +104,7 @@ class StylePropertyMap {
98
104
  _styleMap = new WeakMap();
99
105
  _isDirty = new WeakMap();
100
106
  _string = new WeakMap();
107
+ _indent = new WeakMap();
101
108
  _transformValue = new WeakMap();
102
109
  class StyleRule {
103
110
  constructor(selectorText, style, transformValue) {
@@ -113,7 +120,15 @@ class StyleRule {
113
120
  this.styleMap.onChange = __privateGet(this, _onChange);
114
121
  }
115
122
  get cssText() {
116
- return `${this.selectorText} { ${this.styleMap} }`;
123
+ return this.toString();
124
+ }
125
+ toString(options = { indent: 0 }) {
126
+ const spaces = " ".repeat(options.indent);
127
+ return `${spaces}${this.selectorText} {
128
+ ${this.styleMap.toString({
129
+ indent: options.indent + 2
130
+ })}
131
+ ${spaces}}`;
117
132
  }
118
133
  }
119
134
  _onChange = new WeakMap();
@@ -129,12 +144,15 @@ class MediaRule {
129
144
  return rule;
130
145
  }
131
146
  get cssText() {
147
+ return this.toString();
148
+ }
149
+ toString() {
132
150
  if (this.rules.length === 0) {
133
151
  return "";
134
152
  }
135
153
  const rules = [];
136
154
  for (const rule of this.rules) {
137
- rules.push(` ${rule.cssText}`);
155
+ rules.push(rule.toString({ indent: 2 }));
138
156
  }
139
157
  let conditionText = "";
140
158
  const { minWidth, maxWidth } = this.options;
@@ -157,12 +175,18 @@ class PlaintextRule {
157
175
  this.styleMap = new StylePropertyMap();
158
176
  this.cssText = cssText;
159
177
  }
178
+ toString() {
179
+ return this.cssText;
180
+ }
160
181
  }
161
182
  class FontFaceRule {
162
183
  constructor(options) {
163
184
  this.options = options;
164
185
  }
165
186
  get cssText() {
187
+ return this.toString();
188
+ }
189
+ toString() {
166
190
  const decls = [];
167
191
  const { fontFamily, fontStyle, fontWeight, fontDisplay, src } = this.options;
168
192
  decls.push(
@@ -86,7 +86,7 @@ const toValue = (styleValue, transformValue) => {
86
86
  return value.value;
87
87
  }
88
88
  if (value.type === "layers") {
89
- return value.value.map((value2) => toValue(value2, transformValue)).join(",");
89
+ return value.value.map((value2) => toValue(value2, transformValue)).join(", ");
90
90
  }
91
91
  if (value.type === "tuple") {
92
92
  return value.value.map((value2) => toValue(value2, transformValue)).join(" ");
package/lib/core/rules.js CHANGED
@@ -16,7 +16,7 @@ var __privateSet = (obj, member, value, setter) => {
16
16
  setter ? setter.call(obj, value) : member.set(obj, value);
17
17
  return value;
18
18
  };
19
- var _styleMap, _isDirty, _string, _transformValue, _onChange, _mediaType;
19
+ var _styleMap, _isDirty, _string, _indent, _transformValue, _onChange, _mediaType;
20
20
  import { toValue } from "./to-value";
21
21
  import { toProperty } from "./to-property";
22
22
  class StylePropertyMap {
@@ -24,6 +24,7 @@ class StylePropertyMap {
24
24
  __privateAdd(this, _styleMap, /* @__PURE__ */ new Map());
25
25
  __privateAdd(this, _isDirty, false);
26
26
  __privateAdd(this, _string, "");
27
+ __privateAdd(this, _indent, 0);
27
28
  __privateAdd(this, _transformValue, void 0);
28
29
  __privateSet(this, _transformValue, transformValue);
29
30
  }
@@ -51,20 +52,25 @@ class StylePropertyMap {
51
52
  __privateSet(this, _isDirty, true);
52
53
  this.onChange?.();
53
54
  }
54
- toString() {
55
- if (__privateGet(this, _isDirty) === false) {
55
+ toString({ indent = 0 } = {}) {
56
+ if (__privateGet(this, _isDirty) === false && indent === __privateGet(this, _indent)) {
56
57
  return __privateGet(this, _string);
57
58
  }
59
+ __privateSet(this, _indent, indent);
58
60
  const block = [];
61
+ const spaces = " ".repeat(indent);
59
62
  for (const [property, value] of __privateGet(this, _styleMap)) {
60
63
  if (value === void 0) {
61
64
  continue;
62
65
  }
63
66
  block.push(
64
- `${toProperty(property)}: ${toValue(value, __privateGet(this, _transformValue))}`
67
+ `${spaces}${toProperty(property)}: ${toValue(
68
+ value,
69
+ __privateGet(this, _transformValue)
70
+ )}`
65
71
  );
66
72
  }
67
- __privateSet(this, _string, block.join("; "));
73
+ __privateSet(this, _string, block.join(";\n"));
68
74
  __privateSet(this, _isDirty, false);
69
75
  return __privateGet(this, _string);
70
76
  }
@@ -72,6 +78,7 @@ class StylePropertyMap {
72
78
  _styleMap = new WeakMap();
73
79
  _isDirty = new WeakMap();
74
80
  _string = new WeakMap();
81
+ _indent = new WeakMap();
75
82
  _transformValue = new WeakMap();
76
83
  class StyleRule {
77
84
  constructor(selectorText, style, transformValue) {
@@ -87,7 +94,15 @@ class StyleRule {
87
94
  this.styleMap.onChange = __privateGet(this, _onChange);
88
95
  }
89
96
  get cssText() {
90
- return `${this.selectorText} { ${this.styleMap} }`;
97
+ return this.toString();
98
+ }
99
+ toString(options = { indent: 0 }) {
100
+ const spaces = " ".repeat(options.indent);
101
+ return `${spaces}${this.selectorText} {
102
+ ${this.styleMap.toString({
103
+ indent: options.indent + 2
104
+ })}
105
+ ${spaces}}`;
91
106
  }
92
107
  }
93
108
  _onChange = new WeakMap();
@@ -103,12 +118,15 @@ class MediaRule {
103
118
  return rule;
104
119
  }
105
120
  get cssText() {
121
+ return this.toString();
122
+ }
123
+ toString() {
106
124
  if (this.rules.length === 0) {
107
125
  return "";
108
126
  }
109
127
  const rules = [];
110
128
  for (const rule of this.rules) {
111
- rules.push(` ${rule.cssText}`);
129
+ rules.push(rule.toString({ indent: 2 }));
112
130
  }
113
131
  let conditionText = "";
114
132
  const { minWidth, maxWidth } = this.options;
@@ -131,12 +149,18 @@ class PlaintextRule {
131
149
  this.styleMap = new StylePropertyMap();
132
150
  this.cssText = cssText;
133
151
  }
152
+ toString() {
153
+ return this.cssText;
154
+ }
134
155
  }
135
156
  class FontFaceRule {
136
157
  constructor(options) {
137
158
  this.options = options;
138
159
  }
139
160
  get cssText() {
161
+ return this.toString();
162
+ }
163
+ toString() {
140
164
  const decls = [];
141
165
  const { fontFamily, fontStyle, fontWeight, fontDisplay, src } = this.options;
142
166
  decls.push(
@@ -63,7 +63,7 @@ const toValue = (styleValue, transformValue) => {
63
63
  return value.value;
64
64
  }
65
65
  if (value.type === "layers") {
66
- return value.value.map((value2) => toValue(value2, transformValue)).join(",");
66
+ return value.value.map((value2) => toValue(value2, transformValue)).join(", ");
67
67
  }
68
68
  if (value.type === "tuple") {
69
69
  return value.value.map((value2) => toValue(value2, transformValue)).join(" ");
@@ -10,7 +10,9 @@ declare class StylePropertyMap {
10
10
  keys(): IterableIterator<"clip" | "top" | "right" | `--${string}` | "accentColor" | "alignContent" | "alignItems" | "alignSelf" | "alignTracks" | "animationComposition" | "animationDelay" | "animationDirection" | "animationDuration" | "animationFillMode" | "animationIterationCount" | "animationName" | "animationPlayState" | "animationTimingFunction" | "animationTimeline" | "appearance" | "aspectRatio" | "backdropFilter" | "backfaceVisibility" | "backgroundAttachment" | "backgroundBlendMode" | "backgroundClip" | "backgroundColor" | "backgroundImage" | "backgroundOrigin" | "backgroundPosition" | "backgroundPositionX" | "backgroundPositionY" | "backgroundRepeat" | "backgroundSize" | "blockOverflow" | "blockSize" | "borderBlockColor" | "borderBlockStyle" | "borderBlockWidth" | "borderBlockEndColor" | "borderBlockEndStyle" | "borderBlockEndWidth" | "borderBlockStartColor" | "borderBlockStartStyle" | "borderBlockStartWidth" | "borderBottomColor" | "borderBottomLeftRadius" | "borderBottomRightRadius" | "borderBottomStyle" | "borderBottomWidth" | "borderCollapse" | "borderEndEndRadius" | "borderEndStartRadius" | "borderImageOutset" | "borderImageRepeat" | "borderImageSlice" | "borderImageSource" | "borderImageWidth" | "borderInlineColor" | "borderInlineStyle" | "borderInlineWidth" | "borderInlineEndColor" | "borderInlineEndStyle" | "borderInlineEndWidth" | "borderInlineStartColor" | "borderInlineStartStyle" | "borderInlineStartWidth" | "borderLeftColor" | "borderLeftStyle" | "borderLeftWidth" | "borderRightColor" | "borderRightStyle" | "borderRightWidth" | "borderSpacing" | "borderStartEndRadius" | "borderStartStartRadius" | "borderTopColor" | "borderTopLeftRadius" | "borderTopRightRadius" | "borderTopStyle" | "borderTopWidth" | "bottom" | "boxDecorationBreak" | "boxShadow" | "boxSizing" | "breakAfter" | "breakBefore" | "breakInside" | "captionSide" | "caretColor" | "caretShape" | "clear" | "clipPath" | "color" | "printColorAdjust" | "colorScheme" | "columnCount" | "columnFill" | "columnGap" | "columnRuleColor" | "columnRuleStyle" | "columnRuleWidth" | "columnSpan" | "columnWidth" | "contain" | "containIntrinsicBlockSize" | "containIntrinsicHeight" | "containIntrinsicInlineSize" | "containIntrinsicWidth" | "content" | "contentVisibility" | "counterIncrement" | "counterReset" | "counterSet" | "cursor" | "direction" | "display" | "emptyCells" | "filter" | "flexBasis" | "flexDirection" | "flexGrow" | "flexShrink" | "flexWrap" | "float" | "fontFamily" | "fontFeatureSettings" | "fontKerning" | "fontLanguageOverride" | "fontOpticalSizing" | "fontVariationSettings" | "fontSize" | "fontSizeAdjust" | "fontStretch" | "fontStyle" | "fontSynthesis" | "fontVariant" | "fontVariantAlternates" | "fontVariantCaps" | "fontVariantEastAsian" | "fontVariantLigatures" | "fontVariantNumeric" | "fontVariantPosition" | "fontWeight" | "forcedColorAdjust" | "gridAutoColumns" | "gridAutoFlow" | "gridAutoRows" | "gridColumnEnd" | "gridColumnStart" | "gridRowEnd" | "gridRowStart" | "gridTemplateAreas" | "gridTemplateColumns" | "gridTemplateRows" | "hangingPunctuation" | "height" | "hyphenateCharacter" | "hyphens" | "imageOrientation" | "imageRendering" | "imageResolution" | "initialLetter" | "initialLetterAlign" | "inlineSize" | "inputSecurity" | "insetBlockEnd" | "insetBlockStart" | "insetInlineEnd" | "insetInlineStart" | "isolation" | "justifyContent" | "justifyItems" | "justifySelf" | "justifyTracks" | "left" | "letterSpacing" | "lineBreak" | "lineClamp" | "lineHeight" | "lineHeightStep" | "listStyleImage" | "listStylePosition" | "listStyleType" | "marginBlockEnd" | "marginBlockStart" | "marginBottom" | "marginInlineEnd" | "marginInlineStart" | "marginLeft" | "marginRight" | "marginTop" | "marginTrim" | "maskBorderMode" | "maskBorderOutset" | "maskBorderRepeat" | "maskBorderSlice" | "maskBorderSource" | "maskBorderWidth" | "maskClip" | "maskComposite" | "maskImage" | "maskMode" | "maskOrigin" | "maskPosition" | "maskRepeat" | "maskSize" | "maskType" | "masonryAutoFlow" | "mathDepth" | "mathShift" | "mathStyle" | "maxBlockSize" | "maxHeight" | "maxInlineSize" | "maxLines" | "maxWidth" | "minBlockSize" | "minHeight" | "minInlineSize" | "minWidth" | "mixBlendMode" | "objectFit" | "objectPosition" | "offsetAnchor" | "offsetDistance" | "offsetPath" | "offsetPosition" | "offsetRotate" | "opacity" | "order" | "orphans" | "outlineColor" | "outlineOffset" | "outlineStyle" | "outlineWidth" | "overflow" | "overflowAnchor" | "overflowBlock" | "overflowClipMargin" | "overflowInline" | "overflowWrap" | "overflowX" | "overflowY" | "overscrollBehavior" | "overscrollBehaviorBlock" | "overscrollBehaviorInline" | "overscrollBehaviorX" | "overscrollBehaviorY" | "paddingBlockEnd" | "paddingBlockStart" | "paddingBottom" | "paddingInlineEnd" | "paddingInlineStart" | "paddingLeft" | "paddingRight" | "paddingTop" | "pageBreakAfter" | "pageBreakBefore" | "pageBreakInside" | "paintOrder" | "perspective" | "perspectiveOrigin" | "pointerEvents" | "position" | "quotes" | "resize" | "rotate" | "rowGap" | "rubyAlign" | "rubyMerge" | "rubyPosition" | "scale" | "scrollbarColor" | "scrollbarGutter" | "scrollbarWidth" | "scrollBehavior" | "scrollMarginBlockStart" | "scrollMarginBlockEnd" | "scrollMarginBottom" | "scrollMarginInlineStart" | "scrollMarginInlineEnd" | "scrollMarginLeft" | "scrollMarginRight" | "scrollMarginTop" | "scrollPaddingBlockStart" | "scrollPaddingBlockEnd" | "scrollPaddingBottom" | "scrollPaddingInlineStart" | "scrollPaddingInlineEnd" | "scrollPaddingLeft" | "scrollPaddingRight" | "scrollPaddingTop" | "scrollSnapAlign" | "scrollSnapStop" | "scrollSnapType" | "scrollTimelineAxis" | "scrollTimelineName" | "shapeImageThreshold" | "shapeMargin" | "shapeOutside" | "tabSize" | "tableLayout" | "textAlign" | "textAlignLast" | "textCombineUpright" | "textDecorationColor" | "textDecorationLine" | "textDecorationSkip" | "textDecorationSkipInk" | "textDecorationStyle" | "textDecorationThickness" | "textEmphasisColor" | "textEmphasisPosition" | "textEmphasisStyle" | "textIndent" | "textJustify" | "textOrientation" | "textOverflow" | "textRendering" | "textShadow" | "textSizeAdjust" | "textTransform" | "textUnderlineOffset" | "textUnderlinePosition" | "touchAction" | "transform" | "transformBox" | "transformOrigin" | "transformStyle" | "transitionDelay" | "transitionDuration" | "transitionProperty" | "transitionTimingFunction" | "translate" | "unicodeBidi" | "userSelect" | "verticalAlign" | "visibility" | "whiteSpace" | "widows" | "width" | "willChange" | "wordBreak" | "wordSpacing" | "wordWrap" | "writingMode" | "zIndex">;
11
11
  delete(property: StyleProperty): void;
12
12
  clear(): void;
13
- toString(): string;
13
+ toString({ indent }?: {
14
+ indent?: number | undefined;
15
+ }): string;
14
16
  }
15
17
  export declare class StyleRule {
16
18
  #private;
@@ -19,6 +21,9 @@ export declare class StyleRule {
19
21
  onChange?: () => void;
20
22
  constructor(selectorText: string, style: Style, transformValue?: TransformValue);
21
23
  get cssText(): string;
24
+ toString(options?: {
25
+ indent: number;
26
+ }): string;
22
27
  }
23
28
  export type MediaRuleOptions = {
24
29
  minWidth?: number;
@@ -32,11 +37,13 @@ export declare class MediaRule {
32
37
  constructor(options?: MediaRuleOptions);
33
38
  insertRule(rule: StyleRule | PlaintextRule): StyleRule | PlaintextRule;
34
39
  get cssText(): string;
40
+ toString(): string;
35
41
  }
36
42
  export declare class PlaintextRule {
37
43
  cssText: string;
38
44
  styleMap: StylePropertyMap;
39
45
  constructor(cssText: string);
46
+ toString(): string;
40
47
  }
41
48
  export type FontFaceOptions = {
42
49
  fontFamily: string;
@@ -49,6 +56,7 @@ export declare class FontFaceRule {
49
56
  options: FontFaceOptions;
50
57
  constructor(options: FontFaceOptions);
51
58
  get cssText(): string;
59
+ toString(): string;
52
60
  }
53
61
  export type AnyRule = StyleRule | MediaRule | PlaintextRule | FontFaceRule;
54
62
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webstudio-is/css-engine",
3
- "version": "0.72.0",
3
+ "version": "0.74.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
  "hyphenate-style-name": "^1.0.4",
10
10
  "react": "^18.2.0",
11
11
  "react-dom": "^18.2.0",
12
- "@webstudio-is/css-data": "^0.72.0",
13
- "@webstudio-is/fonts": "^0.72.0"
12
+ "@webstudio-is/css-data": "^0.74.0",
13
+ "@webstudio-is/fonts": "^0.74.0"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@jest/globals": "^29.3.1",
@@ -19,7 +19,7 @@
19
19
  "@types/react": "^18.0.35",
20
20
  "@types/react-dom": "^18.0.11",
21
21
  "jest": "^29.3.1",
22
- "typescript": "5.0.3",
22
+ "typescript": "5.1.3",
23
23
  "@webstudio-is/jest-config": "^1.0.6",
24
24
  "@webstudio-is/scripts": "^0.0.0",
25
25
  "@webstudio-is/storybook-config": "^0.0.0",
@@ -37,7 +37,9 @@ describe("CssEngine", () => {
37
37
  });
38
38
  expect(engine.cssText).toMatchInlineSnapshot(`
39
39
  "@media all and (min-width: 0px) {
40
- .c1 { color: red }
40
+ .c1 {
41
+ color: red
42
+ }
41
43
  }"
42
44
  `);
43
45
  });
@@ -50,7 +52,9 @@ describe("CssEngine", () => {
50
52
  });
51
53
  expect(engine.cssText).toMatchInlineSnapshot(`
52
54
  "@media all and (max-width: 1000px) {
53
- .c1 { color: red }
55
+ .c1 {
56
+ color: red
57
+ }
54
58
  }"
55
59
  `);
56
60
  });
@@ -63,7 +67,9 @@ describe("CssEngine", () => {
63
67
  });
64
68
  expect(engine.cssText).toMatchInlineSnapshot(`
65
69
  "@media all and (min-width: 360px) and (max-width: 1000px) {
66
- .c1 { color: red }
70
+ .c1 {
71
+ color: red
72
+ }
67
73
  }"
68
74
  `);
69
75
  });
@@ -75,7 +81,9 @@ describe("CssEngine", () => {
75
81
  });
76
82
  expect(engine.cssText).toMatchInlineSnapshot(`
77
83
  "@media all {
78
- .c { display: block }
84
+ .c {
85
+ display: block
86
+ }
79
87
  }"
80
88
  `);
81
89
  engine.addStyleRule(".c1", {
@@ -84,8 +92,12 @@ describe("CssEngine", () => {
84
92
  });
85
93
  expect(engine.cssText).toMatchInlineSnapshot(`
86
94
  "@media all {
87
- .c { display: block }
88
- .c1 { color: red }
95
+ .c {
96
+ display: block
97
+ }
98
+ .c1 {
99
+ color: red
100
+ }
89
101
  }"
90
102
  `);
91
103
 
@@ -97,11 +109,17 @@ describe("CssEngine", () => {
97
109
  // Default media query should allways be the first to have the lowest source order specificity
98
110
  expect(engine.cssText).toMatchInlineSnapshot(`
99
111
  "@media all {
100
- .c { display: block }
101
- .c1 { color: red }
112
+ .c {
113
+ display: block
114
+ }
115
+ .c1 {
116
+ color: red
117
+ }
102
118
  }
103
119
  @media all and (min-width: 0px) {
104
- .c1 { color: blue }
120
+ .c1 {
121
+ color: blue
122
+ }
105
123
  }"
106
124
  `);
107
125
  });
@@ -127,13 +145,19 @@ describe("CssEngine", () => {
127
145
  // Default media query should allways be the first to have the lowest source order specificity
128
146
  expect(engine.cssText).toMatchInlineSnapshot(`
129
147
  "@media all {
130
- .c3 { display: block }
148
+ .c3 {
149
+ display: block
150
+ }
131
151
  }
132
152
  @media all and (min-width: 0px) {
133
- .c1 { display: block }
153
+ .c1 {
154
+ display: block
155
+ }
134
156
  }
135
157
  @media all and (min-width: 300px) {
136
- .c2 { display: flex }
158
+ .c2 {
159
+ display: flex
160
+ }
137
161
  }"
138
162
  `);
139
163
  });
@@ -149,8 +173,12 @@ describe("CssEngine", () => {
149
173
  });
150
174
  expect(engine.cssText).toMatchInlineSnapshot(`
151
175
  "@media all {
152
- .c0 { display: block }
153
- .c1 { display: flex }
176
+ .c0 {
177
+ display: block
178
+ }
179
+ .c1 {
180
+ display: flex
181
+ }
154
182
  }"
155
183
  `);
156
184
 
@@ -166,8 +194,12 @@ describe("CssEngine", () => {
166
194
  });
167
195
  expect(engine.cssText).toMatchInlineSnapshot(`
168
196
  "@media all {
169
- .c1 { display: flex }
170
- .c0 { display: block }
197
+ .c1 {
198
+ display: flex
199
+ }
200
+ .c0 {
201
+ display: block
202
+ }
171
203
  }"
172
204
  `);
173
205
  });
@@ -183,7 +215,10 @@ describe("CssEngine", () => {
183
215
  });
184
216
  expect(engine.cssText).toMatchInlineSnapshot(`
185
217
  "@media all and (min-width: 0px) {
186
- .c { display: block; color: red }
218
+ .c {
219
+ display: block;
220
+ color: red
221
+ }
187
222
  }"
188
223
  `);
189
224
  });
@@ -198,7 +233,9 @@ describe("CssEngine", () => {
198
233
  });
199
234
  expect(engine.cssText).toMatchInlineSnapshot(`
200
235
  "@media all and (min-width: 0px) {
201
- .c { background-color: red }
236
+ .c {
237
+ background-color: red
238
+ }
202
239
  }"
203
240
  `);
204
241
  });
@@ -214,12 +251,18 @@ describe("CssEngine", () => {
214
251
  });
215
252
  expect(engine.cssText).toMatchInlineSnapshot(`
216
253
  "@media all and (min-width: 0px) {
217
- .c { display: block; color: red }
254
+ .c {
255
+ display: block;
256
+ color: red
257
+ }
258
+ }"
259
+ `);
260
+ expect(rule1.cssText).toMatchInlineSnapshot(`
261
+ ".c {
262
+ display: block;
263
+ color: red
218
264
  }"
219
265
  `);
220
- expect(rule1.cssText).toMatchInlineSnapshot(
221
- `".c { display: block; color: red }"`
222
- );
223
266
  engine.addStyleRule(".c2", {
224
267
  style: {
225
268
  ...style0,
@@ -229,8 +272,14 @@ describe("CssEngine", () => {
229
272
  });
230
273
  expect(engine.cssText).toMatchInlineSnapshot(`
231
274
  "@media all and (min-width: 0px) {
232
- .c { display: block; color: red }
233
- .c2 { display: block; color: green }
275
+ .c {
276
+ display: block;
277
+ color: red
278
+ }
279
+ .c2 {
280
+ display: block;
281
+ color: green
282
+ }
234
283
  }"
235
284
  `);
236
285
  });
@@ -246,22 +295,34 @@ describe("CssEngine", () => {
246
295
  });
247
296
  expect(engine.cssText).toMatchInlineSnapshot(`
248
297
  "@media all and (min-width: 0px) {
249
- .c { display: block; color: red }
298
+ .c {
299
+ display: block;
300
+ color: red
301
+ }
302
+ }"
303
+ `);
304
+ expect(rule.cssText).toMatchInlineSnapshot(`
305
+ ".c {
306
+ display: block;
307
+ color: red
250
308
  }"
251
309
  `);
252
- expect(rule.cssText).toMatchInlineSnapshot(
253
- `".c { display: block; color: red }"`
254
- );
255
310
 
256
311
  rule.styleMap.set("color", { type: "keyword", value: "green" });
257
312
 
258
- expect(rule.cssText).toMatchInlineSnapshot(
259
- `".c { display: block; color: green }"`
260
- );
313
+ expect(rule.cssText).toMatchInlineSnapshot(`
314
+ ".c {
315
+ display: block;
316
+ color: green
317
+ }"
318
+ `);
261
319
 
262
320
  expect(engine.cssText).toMatchInlineSnapshot(`
263
321
  "@media all and (min-width: 0px) {
264
- .c { display: block; color: green }
322
+ .c {
323
+ display: block;
324
+ color: green
325
+ }
265
326
  }"
266
327
  `);
267
328
  });
@@ -276,13 +337,17 @@ describe("CssEngine", () => {
276
337
  });
277
338
  expect(engine.cssText).toMatchInlineSnapshot(`
278
339
  "@media all and (min-width: 0px) {
279
- .c { color: red }
340
+ .c {
341
+ color: red
342
+ }
280
343
  }"
281
344
  `);
282
345
  engine.addMediaRule(mediaId0, { minWidth: 10 });
283
346
  expect(engine.cssText).toMatchInlineSnapshot(`
284
347
  "@media all and (min-width: 10px) {
285
- .c { color: red }
348
+ .c {
349
+ color: red
350
+ }
286
351
  }"
287
352
  `);
288
353
  });
@@ -295,13 +360,17 @@ describe("CssEngine", () => {
295
360
  });
296
361
  expect(engine.cssText).toMatchInlineSnapshot(`
297
362
  "@media all and (min-width: 0px) {
298
- .c { display: block }
363
+ .c {
364
+ display: block
365
+ }
299
366
  }"
300
367
  `);
301
368
  engine.addMediaRule(mediaId0, mediaRuleOptions0);
302
369
  expect(engine.cssText).toMatchInlineSnapshot(`
303
370
  "@media all and (min-width: 0px) {
304
- .c { display: block }
371
+ .c {
372
+ display: block
373
+ }
305
374
  }"
306
375
  `);
307
376
  });
@@ -366,7 +435,9 @@ describe("CssEngine", () => {
366
435
  rule.styleMap.delete("display");
367
436
  expect(engine.cssText).toMatchInlineSnapshot(`
368
437
  "@media all {
369
- .c { color: black }
438
+ .c {
439
+ color: black
440
+ }
370
441
  }"
371
442
  `);
372
443
  });
@@ -408,7 +479,9 @@ describe("CssEngine", () => {
408
479
  rule.styleMap.delete("display");
409
480
  expect(engine.cssText).toMatchInlineSnapshot(`
410
481
  "@media all {
411
- .c { background-image: url(foo.png) }
482
+ .c {
483
+ background-image: url(foo.png)
484
+ }
412
485
  }"
413
486
  `);
414
487
  });
package/src/core/rules.ts CHANGED
@@ -6,6 +6,7 @@ class StylePropertyMap {
6
6
  #styleMap: Map<StyleProperty, StyleValue | undefined> = new Map();
7
7
  #isDirty = false;
8
8
  #string = "";
9
+ #indent = 0;
9
10
  #transformValue?: TransformValue;
10
11
  onChange?: () => void;
11
12
  constructor(transformValue?: TransformValue) {
@@ -35,20 +36,25 @@ class StylePropertyMap {
35
36
  this.#isDirty = true;
36
37
  this.onChange?.();
37
38
  }
38
- toString() {
39
- if (this.#isDirty === false) {
39
+ toString({ indent = 0 } = {}) {
40
+ if (this.#isDirty === false && indent === this.#indent) {
40
41
  return this.#string;
41
42
  }
43
+ this.#indent = indent;
42
44
  const block: Array<string> = [];
45
+ const spaces = " ".repeat(indent);
43
46
  for (const [property, value] of this.#styleMap) {
44
47
  if (value === undefined) {
45
48
  continue;
46
49
  }
47
50
  block.push(
48
- `${toProperty(property)}: ${toValue(value, this.#transformValue)}`
51
+ `${spaces}${toProperty(property)}: ${toValue(
52
+ value,
53
+ this.#transformValue
54
+ )}`
49
55
  );
50
56
  }
51
- this.#string = block.join("; ");
57
+ this.#string = block.join(";\n");
52
58
  this.#isDirty = false;
53
59
  return this.#string;
54
60
  }
@@ -75,7 +81,13 @@ export class StyleRule {
75
81
  this.onChange?.();
76
82
  };
77
83
  get cssText() {
78
- return `${this.selectorText} { ${this.styleMap} }`;
84
+ return this.toString();
85
+ }
86
+ toString(options = { indent: 0 }) {
87
+ const spaces = " ".repeat(options.indent);
88
+ return `${spaces}${this.selectorText} {\n${this.styleMap.toString({
89
+ indent: options.indent + 2,
90
+ })}\n${spaces}}`;
79
91
  }
80
92
  }
81
93
 
@@ -98,12 +110,15 @@ export class MediaRule {
98
110
  return rule;
99
111
  }
100
112
  get cssText() {
113
+ return this.toString();
114
+ }
115
+ toString() {
101
116
  if (this.rules.length === 0) {
102
117
  return "";
103
118
  }
104
119
  const rules = [];
105
120
  for (const rule of this.rules) {
106
- rules.push(` ${rule.cssText}`);
121
+ rules.push(rule.toString({ indent: 2 }));
107
122
  }
108
123
  let conditionText = "";
109
124
  const { minWidth, maxWidth } = this.options;
@@ -125,6 +140,9 @@ export class PlaintextRule {
125
140
  constructor(cssText: string) {
126
141
  this.cssText = cssText;
127
142
  }
143
+ toString() {
144
+ return this.cssText;
145
+ }
128
146
  }
129
147
 
130
148
  export type FontFaceOptions = {
@@ -141,6 +159,9 @@ export class FontFaceRule {
141
159
  this.options = options;
142
160
  }
143
161
  get cssText() {
162
+ return this.toString();
163
+ }
164
+ toString() {
144
165
  const decls = [];
145
166
  const { fontFamily, fontStyle, fontWeight, fontDisplay, src } =
146
167
  this.options;
@@ -116,7 +116,7 @@ describe("Convert WS CSS Values to native CSS strings", () => {
116
116
  }
117
117
  );
118
118
 
119
- expect(value).toBe("auto,10px,calc(10px),url(foo.png)");
119
+ expect(value).toBe("auto, 10px, calc(10px), url(foo.png)");
120
120
  });
121
121
 
122
122
  test("tuple", () => {
@@ -91,7 +91,9 @@ export const toValue = (
91
91
  }
92
92
 
93
93
  if (value.type === "layers") {
94
- return value.value.map((value) => toValue(value, transformValue)).join(",");
94
+ return value.value
95
+ .map((value) => toValue(value, transformValue))
96
+ .join(", ");
95
97
  }
96
98
 
97
99
  if (value.type === "tuple") {