sheetom 0.1.0-rc.0 → 0.1.0-rc.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 +17 -5
- package/compatibility/baselines/0.1.0-rc.1.json +108 -0
- package/compatibility/schemas/compatibility-report.schema.json +17 -1
- package/dist/index.cjs +199 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +199 -34
- package/dist/index.js.map +1 -1
- package/docs/api.md +11 -0
- package/docs/releasing.md +6 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,9 +9,7 @@ not implement the DOM, cascade, selector matching, layout, or computed styles.
|
|
|
9
9
|
|
|
10
10
|
## Install
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
`0.1.0-rc.0` under the `next` dist-tag after the complete conformance gate
|
|
14
|
-
passes. Once published:
|
|
12
|
+
Install the current release candidate from the `next` dist-tag:
|
|
15
13
|
|
|
16
14
|
```sh
|
|
17
15
|
npm install sheetom@next
|
|
@@ -59,9 +57,10 @@ Constructed sheets follow browser replacement behavior and strip `@import`.
|
|
|
59
57
|
`parseStyleSheet` creates a regular authoring sheet and preserves valid imports
|
|
60
58
|
without loading them.
|
|
61
59
|
|
|
62
|
-
## Recovered values and
|
|
60
|
+
## Recovered values and reparsable serialization
|
|
63
61
|
|
|
64
|
-
SheetOM distinguishes
|
|
62
|
+
SheetOM distinguishes browser-facing CSSOM text from reparsable stylesheet
|
|
63
|
+
output. Chromium
|
|
65
64
|
accepts some values using CSS Syntax end-of-input recovery while preserving the
|
|
66
65
|
unclosed input in `getPropertyValue()` and `cssText`:
|
|
67
66
|
|
|
@@ -82,6 +81,19 @@ sheet.serialize();
|
|
|
82
81
|
// reparsable CSS with the missing syntax repaired
|
|
83
82
|
```
|
|
84
83
|
|
|
84
|
+
The three text surfaces have distinct contracts:
|
|
85
|
+
|
|
86
|
+
| Surface | Contract |
|
|
87
|
+
| --- | --- |
|
|
88
|
+
| `rule.style.cssText` | Browser-compatible observable declaration text, including accepted recovery quirks. |
|
|
89
|
+
| `rule.cssText` | Browser-compatible observable text for one rule and its current descendants. |
|
|
90
|
+
| `sheet.serialize()` | Deterministic, reparsable stylesheet output with recoverable syntax confined to its declaration or rule. |
|
|
91
|
+
|
|
92
|
+
Reparsable serialization means that the output can be parsed again without one
|
|
93
|
+
declaration leaking into the next declaration or rule, while preserving the
|
|
94
|
+
valid semantic state owned by SheetOM. It does not sanitize URLs, enforce CSP,
|
|
95
|
+
block remote resources, or make valid hostile CSS safe to render.
|
|
96
|
+
|
|
85
97
|
Invalid `setProperty` values and priorities are atomic no-ops, matching browser
|
|
86
98
|
behavior. Opt into mutation diagnostics with `{ diagnostics: true }` and drain
|
|
87
99
|
them using `takeDiagnostics()`.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../schemas/compatibility-report.schema.json",
|
|
3
|
+
"schemaVersion": 1,
|
|
4
|
+
"packageVersion": "0.1.0-rc.1",
|
|
5
|
+
"baseline": {
|
|
6
|
+
"wptCommit": "54f8f933629e7c010ae98a246729af01f8abcda5",
|
|
7
|
+
"lightningcss": "1.33.0",
|
|
8
|
+
"cssTree": "3.2.1",
|
|
9
|
+
"runtimes": {
|
|
10
|
+
"node": "v24.18.0",
|
|
11
|
+
"bun": "1.3.1",
|
|
12
|
+
"deno": "2.9.5"
|
|
13
|
+
},
|
|
14
|
+
"browsers": {
|
|
15
|
+
"chromium": "151.0.7922.34 (revision 1234)",
|
|
16
|
+
"firefox": "153.0 (revision 1538)",
|
|
17
|
+
"webkit": "26.5 (revision 2336)"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"summary": {
|
|
21
|
+
"passed": 5,
|
|
22
|
+
"divergences": 2,
|
|
23
|
+
"excluded": 0,
|
|
24
|
+
"unexplained": 0
|
|
25
|
+
},
|
|
26
|
+
"evidence": {
|
|
27
|
+
"operationFixtures": {
|
|
28
|
+
"passed": 7,
|
|
29
|
+
"total": 7,
|
|
30
|
+
"sha256": "b4ec45fce8ad92393966d1b538748f6b3256d7d51af2fb0f76b4539a3300c349",
|
|
31
|
+
"adapters": [
|
|
32
|
+
{
|
|
33
|
+
"adapter": "sheetom",
|
|
34
|
+
"passed": 7,
|
|
35
|
+
"total": 7
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"adapter": "chromium",
|
|
39
|
+
"passed": 7,
|
|
40
|
+
"total": 7
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"adapter": "firefox",
|
|
44
|
+
"passed": 7,
|
|
45
|
+
"total": 7
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"adapter": "webkit",
|
|
49
|
+
"passed": 7,
|
|
50
|
+
"total": 7
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
"nativeWpt": [
|
|
55
|
+
{
|
|
56
|
+
"engine": "chrome",
|
|
57
|
+
"version": "150.0.7871.128",
|
|
58
|
+
"sha256": "33aaf2418507bb1d335cd5350b21a86cee86cbe3059e9eb1b2001c9e2693c1f1",
|
|
59
|
+
"passed": 4,
|
|
60
|
+
"total": 4
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"engine": "firefox",
|
|
64
|
+
"version": "153.0.3",
|
|
65
|
+
"sha256": "20ee01df1d83cc67a50e7949ff6a62c87c572c2fb0159ee5c2b15bc8e9a13a1f",
|
|
66
|
+
"passed": 4,
|
|
67
|
+
"total": 4
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
"resolutions": [
|
|
72
|
+
{
|
|
73
|
+
"fixtureId": "declarations.basic-set-property",
|
|
74
|
+
"decision": "shared",
|
|
75
|
+
"rationale": "The observable declaration state is shared by the pinned browser engines."
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"fixtureId": "declarations.malformed-padding",
|
|
79
|
+
"decision": "chromium-fallback",
|
|
80
|
+
"rationale": "CSS Syntax accepts the EOF-recovered value; Chromium preserves the unclosed observable spelling while Firefox materializes closing parentheses."
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"fixtureId": "declarations.setproperty-null-undefined",
|
|
84
|
+
"decision": "shared",
|
|
85
|
+
"rationale": "The four adapted WPT subtests define the shared null and undefined conversion behavior."
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"fixtureId": "declarations.required-arguments",
|
|
89
|
+
"decision": "shared",
|
|
90
|
+
"rationale": "Required WebIDL arguments throw TypeError before string conversion in the pinned browser engines."
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"fixtureId": "declarations.set-property-multiple-declarations",
|
|
94
|
+
"decision": "shared",
|
|
95
|
+
"rationale": "A setProperty value is parsed independently and cannot escape into another declaration; invalid input leaves the previous declaration unchanged."
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"fixtureId": "declarations.escaped-custom-property-name",
|
|
99
|
+
"decision": "shared",
|
|
100
|
+
"rationale": "Custom-property method arguments retain their logical names and cssText escapes those names only when serializing CSS syntax."
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"fixtureId": "integration.weweb-authoring-tree",
|
|
104
|
+
"decision": "chromium-fallback",
|
|
105
|
+
"rationale": "The standard rule-tree operations and detachment identity are shared; the recovered padding spelling follows the existing Chromium fallback resolution."
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
}
|
|
@@ -43,7 +43,23 @@
|
|
|
43
43
|
"required": ["passed", "total"],
|
|
44
44
|
"properties": {
|
|
45
45
|
"passed": { "type": "integer", "minimum": 0 },
|
|
46
|
-
"total": { "type": "integer", "minimum": 0 }
|
|
46
|
+
"total": { "type": "integer", "minimum": 0 },
|
|
47
|
+
"sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
|
|
48
|
+
"adapters": {
|
|
49
|
+
"type": "array",
|
|
50
|
+
"items": {
|
|
51
|
+
"type": "object",
|
|
52
|
+
"additionalProperties": false,
|
|
53
|
+
"required": ["adapter", "passed", "total"],
|
|
54
|
+
"properties": {
|
|
55
|
+
"adapter": {
|
|
56
|
+
"enum": ["sheetom", "chromium", "firefox", "webkit"]
|
|
57
|
+
},
|
|
58
|
+
"passed": { "type": "integer", "minimum": 0 },
|
|
59
|
+
"total": { "type": "integer", "minimum": 0 }
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
47
63
|
}
|
|
48
64
|
},
|
|
49
65
|
"nativeWpt": {
|
package/dist/index.cjs
CHANGED
|
@@ -52,7 +52,7 @@ var DeclarationBlock = class {
|
|
|
52
52
|
});
|
|
53
53
|
};
|
|
54
54
|
for (const declaration of declarations) {
|
|
55
|
-
const name =
|
|
55
|
+
const name = this.#codec.normalizeName(declaration.name);
|
|
56
56
|
const propertyValue = this.#codec.parseValue(name, declaration.value);
|
|
57
57
|
if (!propertyValue) {
|
|
58
58
|
sourceIndex += 1;
|
|
@@ -110,19 +110,19 @@ var DeclarationBlock = class {
|
|
|
110
110
|
return this.#records[this.#codec.normalizeIndex(index)]?.name ?? "";
|
|
111
111
|
}
|
|
112
112
|
getPropertyValue(name) {
|
|
113
|
-
const normalizedName =
|
|
113
|
+
const normalizedName = this.#codec.normalizeName(name);
|
|
114
114
|
const shorthand = this.#shorthand(normalizedName, false);
|
|
115
115
|
if (shorthand) return shorthand.value;
|
|
116
116
|
return this.#records.find((record) => record.name === normalizedName)?.observableValue ?? "";
|
|
117
117
|
}
|
|
118
118
|
getPropertyPriority(name) {
|
|
119
|
-
const normalizedName =
|
|
119
|
+
const normalizedName = this.#codec.normalizeName(name);
|
|
120
120
|
const shorthand = this.#shorthand(normalizedName, false);
|
|
121
121
|
if (shorthand) return shorthand.important ? "important" : "";
|
|
122
122
|
return this.#records.find((record) => record.name === normalizedName)?.important ? "important" : "";
|
|
123
123
|
}
|
|
124
124
|
setProperty(name, value, priority) {
|
|
125
|
-
const normalizedName =
|
|
125
|
+
const normalizedName = this.#codec.normalizeName(name);
|
|
126
126
|
const normalizedPriority = priority.toLowerCase();
|
|
127
127
|
if (normalizedPriority !== "" && normalizedPriority !== "important") {
|
|
128
128
|
this.#reportDiagnostic("INVALID_PRIORITY", normalizedName, priority);
|
|
@@ -160,7 +160,7 @@ var DeclarationBlock = class {
|
|
|
160
160
|
this.#commitRecord(normalizedName, parsed, important);
|
|
161
161
|
}
|
|
162
162
|
removeProperty(name) {
|
|
163
|
-
const normalizedName =
|
|
163
|
+
const normalizedName = this.#codec.normalizeName(name);
|
|
164
164
|
const longhands = this.#codec.shorthandLonghands(normalizedName);
|
|
165
165
|
if (longhands) {
|
|
166
166
|
const previousValue = this.getPropertyValue(normalizedName);
|
|
@@ -1110,6 +1110,109 @@ const chromiumSupportedProperties = /* @__PURE__ */ new Set([
|
|
|
1110
1110
|
"z-index",
|
|
1111
1111
|
"zoom"
|
|
1112
1112
|
]);
|
|
1113
|
+
const chromiumPropertyAliases = {
|
|
1114
|
+
"-webkit-align-content": "align-content",
|
|
1115
|
+
"-webkit-align-items": "align-items",
|
|
1116
|
+
"-webkit-align-self": "align-self",
|
|
1117
|
+
"-webkit-animation-delay": "animation-delay",
|
|
1118
|
+
"-webkit-animation-direction": "animation-direction",
|
|
1119
|
+
"-webkit-animation-duration": "animation-duration",
|
|
1120
|
+
"-webkit-animation-fill-mode": "animation-fill-mode",
|
|
1121
|
+
"-webkit-animation-iteration-count": "animation-iteration-count",
|
|
1122
|
+
"-webkit-animation-name": "animation-name",
|
|
1123
|
+
"-webkit-animation-play-state": "animation-play-state",
|
|
1124
|
+
"-webkit-animation-timing-function": "animation-timing-function",
|
|
1125
|
+
"-webkit-app-region": "app-region",
|
|
1126
|
+
"-webkit-appearance": "appearance",
|
|
1127
|
+
"-webkit-backface-visibility": "backface-visibility",
|
|
1128
|
+
"-webkit-background-clip": "background-clip",
|
|
1129
|
+
"-webkit-background-origin": "background-origin",
|
|
1130
|
+
"-webkit-background-size": "background-size",
|
|
1131
|
+
"-webkit-border-after-color": "border-block-end-color",
|
|
1132
|
+
"-webkit-border-after-style": "border-block-end-style",
|
|
1133
|
+
"-webkit-border-after-width": "border-block-end-width",
|
|
1134
|
+
"-webkit-border-before-color": "border-block-start-color",
|
|
1135
|
+
"-webkit-border-before-style": "border-block-start-style",
|
|
1136
|
+
"-webkit-border-before-width": "border-block-start-width",
|
|
1137
|
+
"-webkit-border-bottom-left-radius": "border-bottom-left-radius",
|
|
1138
|
+
"-webkit-border-bottom-right-radius": "border-bottom-right-radius",
|
|
1139
|
+
"-webkit-border-end-color": "border-inline-end-color",
|
|
1140
|
+
"-webkit-border-end-style": "border-inline-end-style",
|
|
1141
|
+
"-webkit-border-end-width": "border-inline-end-width",
|
|
1142
|
+
"-webkit-border-start-color": "border-inline-start-color",
|
|
1143
|
+
"-webkit-border-start-style": "border-inline-start-style",
|
|
1144
|
+
"-webkit-border-start-width": "border-inline-start-width",
|
|
1145
|
+
"-webkit-border-top-left-radius": "border-top-left-radius",
|
|
1146
|
+
"-webkit-border-top-right-radius": "border-top-right-radius",
|
|
1147
|
+
"-webkit-box-shadow": "box-shadow",
|
|
1148
|
+
"-webkit-box-sizing": "box-sizing",
|
|
1149
|
+
"-webkit-clip-path": "clip-path",
|
|
1150
|
+
"-webkit-column-break-after": "break-after",
|
|
1151
|
+
"-webkit-column-break-before": "break-before",
|
|
1152
|
+
"-webkit-column-break-inside": "break-inside",
|
|
1153
|
+
"-webkit-column-count": "column-count",
|
|
1154
|
+
"-webkit-column-gap": "column-gap",
|
|
1155
|
+
"-webkit-column-rule-color": "column-rule-color",
|
|
1156
|
+
"-webkit-column-rule-style": "column-rule-style",
|
|
1157
|
+
"-webkit-column-rule-width": "column-rule-width",
|
|
1158
|
+
"-webkit-column-span": "column-span",
|
|
1159
|
+
"-webkit-column-width": "column-width",
|
|
1160
|
+
"-webkit-filter": "filter",
|
|
1161
|
+
"-webkit-flex-basis": "flex-basis",
|
|
1162
|
+
"-webkit-flex-direction": "flex-direction",
|
|
1163
|
+
"-webkit-flex-grow": "flex-grow",
|
|
1164
|
+
"-webkit-flex-shrink": "flex-shrink",
|
|
1165
|
+
"-webkit-flex-wrap": "flex-wrap",
|
|
1166
|
+
"-webkit-font-feature-settings": "font-feature-settings",
|
|
1167
|
+
"-webkit-hyphenate-character": "hyphenate-character",
|
|
1168
|
+
"-webkit-justify-content": "justify-content",
|
|
1169
|
+
"-webkit-logical-height": "block-size",
|
|
1170
|
+
"-webkit-logical-width": "inline-size",
|
|
1171
|
+
"-webkit-margin-after": "margin-block-end",
|
|
1172
|
+
"-webkit-margin-before": "margin-block-start",
|
|
1173
|
+
"-webkit-margin-end": "margin-inline-end",
|
|
1174
|
+
"-webkit-margin-start": "margin-inline-start",
|
|
1175
|
+
"-webkit-mask-clip": "mask-clip",
|
|
1176
|
+
"-webkit-mask-composite": "mask-composite",
|
|
1177
|
+
"-webkit-mask-image": "mask-image",
|
|
1178
|
+
"-webkit-mask-origin": "mask-origin",
|
|
1179
|
+
"-webkit-mask-repeat": "mask-repeat",
|
|
1180
|
+
"-webkit-mask-size": "mask-size",
|
|
1181
|
+
"-webkit-max-logical-height": "max-block-size",
|
|
1182
|
+
"-webkit-max-logical-width": "max-inline-size",
|
|
1183
|
+
"-webkit-min-logical-height": "min-block-size",
|
|
1184
|
+
"-webkit-min-logical-width": "min-inline-size",
|
|
1185
|
+
"-webkit-opacity": "opacity",
|
|
1186
|
+
"-webkit-order": "order",
|
|
1187
|
+
"-webkit-padding-after": "padding-block-end",
|
|
1188
|
+
"-webkit-padding-before": "padding-block-start",
|
|
1189
|
+
"-webkit-padding-end": "padding-inline-end",
|
|
1190
|
+
"-webkit-padding-start": "padding-inline-start",
|
|
1191
|
+
"-webkit-perspective": "perspective",
|
|
1192
|
+
"-webkit-perspective-origin": "perspective-origin",
|
|
1193
|
+
"-webkit-print-color-adjust": "print-color-adjust",
|
|
1194
|
+
"-webkit-shape-image-threshold": "shape-image-threshold",
|
|
1195
|
+
"-webkit-shape-margin": "shape-margin",
|
|
1196
|
+
"-webkit-shape-outside": "shape-outside",
|
|
1197
|
+
"-webkit-text-emphasis-color": "text-emphasis-color",
|
|
1198
|
+
"-webkit-text-emphasis-position": "text-emphasis-position",
|
|
1199
|
+
"-webkit-text-emphasis-style": "text-emphasis-style",
|
|
1200
|
+
"-webkit-text-size-adjust": "text-size-adjust",
|
|
1201
|
+
"-webkit-transform": "transform",
|
|
1202
|
+
"-webkit-transform-origin": "transform-origin",
|
|
1203
|
+
"-webkit-transform-style": "transform-style",
|
|
1204
|
+
"-webkit-transition-delay": "transition-delay",
|
|
1205
|
+
"-webkit-transition-duration": "transition-duration",
|
|
1206
|
+
"-webkit-transition-property": "transition-property",
|
|
1207
|
+
"-webkit-transition-timing-function": "transition-timing-function",
|
|
1208
|
+
"-webkit-user-select": "user-select",
|
|
1209
|
+
"grid-column-gap": "column-gap",
|
|
1210
|
+
"grid-row-gap": "row-gap",
|
|
1211
|
+
"page-break-after": "break-after",
|
|
1212
|
+
"page-break-before": "break-before",
|
|
1213
|
+
"page-break-inside": "break-inside",
|
|
1214
|
+
"word-wrap": "overflow-wrap"
|
|
1215
|
+
};
|
|
1113
1216
|
const chromiumShorthandLonghands = {
|
|
1114
1217
|
"-webkit-align-content": ["align-content"],
|
|
1115
1218
|
"-webkit-align-items": ["align-items"],
|
|
@@ -2016,6 +2119,18 @@ function analyzeSubstitutions(value) {
|
|
|
2016
2119
|
valid: true
|
|
2017
2120
|
};
|
|
2018
2121
|
let found = value.type === "var" || value.type === "env";
|
|
2122
|
+
if (found) {
|
|
2123
|
+
const details = value.value;
|
|
2124
|
+
if (!isUnknownRecord(details)) return {
|
|
2125
|
+
found: true,
|
|
2126
|
+
valid: false
|
|
2127
|
+
};
|
|
2128
|
+
const fallback = details.fallback;
|
|
2129
|
+
if (Array.isArray(fallback) && fallback.some((item) => isToken(item, "semicolon") || isToken(item, "delim", "!"))) return {
|
|
2130
|
+
found: true,
|
|
2131
|
+
valid: false
|
|
2132
|
+
};
|
|
2133
|
+
}
|
|
2019
2134
|
const cssFunction = functionValue(value);
|
|
2020
2135
|
if (cssFunction?.name === "attr") {
|
|
2021
2136
|
found = true;
|
|
@@ -2076,14 +2191,6 @@ function serializeIdentifier(value) {
|
|
|
2076
2191
|
}
|
|
2077
2192
|
return result;
|
|
2078
2193
|
}
|
|
2079
|
-
function containsPriorityDelimiter(value) {
|
|
2080
|
-
let found = false;
|
|
2081
|
-
css_tree.tokenize(value, (type, start, end) => {
|
|
2082
|
-
if (css_tree.tokenNames[type] !== "delim-token") return;
|
|
2083
|
-
if (value.slice(start, end) === "!") found = true;
|
|
2084
|
-
});
|
|
2085
|
-
return found;
|
|
2086
|
-
}
|
|
2087
2194
|
function canonicalizeLeadingDecimal(value) {
|
|
2088
2195
|
const replacements = [];
|
|
2089
2196
|
css_tree.tokenize(value, (type, start, end) => {
|
|
@@ -2109,32 +2216,80 @@ function canonicalizeLeadingDecimal(value) {
|
|
|
2109
2216
|
}
|
|
2110
2217
|
return result;
|
|
2111
2218
|
}
|
|
2219
|
+
function containsTopLevelDeclarationBoundary(value) {
|
|
2220
|
+
let depth = 0;
|
|
2221
|
+
let found = false;
|
|
2222
|
+
css_tree.tokenize(value, (type, start, end) => {
|
|
2223
|
+
const tokenName = css_tree.tokenNames[type];
|
|
2224
|
+
if (tokenName === void 0) return;
|
|
2225
|
+
if ([
|
|
2226
|
+
"function-token",
|
|
2227
|
+
"(-token",
|
|
2228
|
+
"[-token",
|
|
2229
|
+
"{-token"
|
|
2230
|
+
].includes(tokenName)) {
|
|
2231
|
+
depth += 1;
|
|
2232
|
+
return;
|
|
2233
|
+
}
|
|
2234
|
+
if ([
|
|
2235
|
+
")-token",
|
|
2236
|
+
"]-token",
|
|
2237
|
+
"}-token"
|
|
2238
|
+
].includes(tokenName)) {
|
|
2239
|
+
if (depth > 0) depth -= 1;
|
|
2240
|
+
return;
|
|
2241
|
+
}
|
|
2242
|
+
if (depth === 0 && (tokenName === "semicolon-token" || tokenName === "delim-token" && value.slice(start, end) === "!")) found = true;
|
|
2243
|
+
});
|
|
2244
|
+
return found;
|
|
2245
|
+
}
|
|
2246
|
+
function canonicalizeBrowserValue(name, value) {
|
|
2247
|
+
if (name === "-webkit-box-reflect" && [
|
|
2248
|
+
"above",
|
|
2249
|
+
"below",
|
|
2250
|
+
"left",
|
|
2251
|
+
"right"
|
|
2252
|
+
].includes(value)) return `${value} 0px`;
|
|
2253
|
+
return value;
|
|
2254
|
+
}
|
|
2112
2255
|
function parsePropertyValue(name, observableValue) {
|
|
2113
2256
|
if (name === "--") return null;
|
|
2114
2257
|
if (!name.startsWith("--") && !chromiumSupportedProperties.has(name)) return null;
|
|
2115
|
-
if (
|
|
2258
|
+
if (containsTopLevelDeclarationBoundary(observableValue)) return null;
|
|
2116
2259
|
let declaration;
|
|
2260
|
+
let declarationCount = 0;
|
|
2117
2261
|
try {
|
|
2118
2262
|
const result = (0, lightningcss.transformStyleAttribute)({
|
|
2119
2263
|
code: encoder$1.encode(`${name.startsWith("--") ? serializeIdentifier(name) : name}: ${observableValue}`),
|
|
2120
2264
|
visitor: { Declaration(candidate) {
|
|
2265
|
+
declarationCount += 1;
|
|
2121
2266
|
declaration = candidate;
|
|
2122
2267
|
} }
|
|
2123
2268
|
});
|
|
2124
|
-
if (!isUnknownRecord(declaration)) return null;
|
|
2269
|
+
if (declarationCount !== 1 || !isUnknownRecord(declaration)) return null;
|
|
2125
2270
|
if (!Object.hasOwn(declaration, "property")) return null;
|
|
2126
2271
|
const pendingSubstitution = declaration.property === "unparsed";
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
if (!analysis.found || !analysis.valid) return null;
|
|
2130
|
-
}
|
|
2272
|
+
const analysis = analyzeSubstitutions(declaration);
|
|
2273
|
+
if (!analysis.valid || pendingSubstitution && !analysis.found) return null;
|
|
2131
2274
|
const serialized = decoder$1.decode(result.code);
|
|
2132
|
-
const
|
|
2133
|
-
|
|
2134
|
-
|
|
2275
|
+
const parsed = css_tree.parse(serialized, {
|
|
2276
|
+
context: "declarationList",
|
|
2277
|
+
positions: true
|
|
2278
|
+
});
|
|
2279
|
+
if (parsed.type !== "DeclarationList") return null;
|
|
2280
|
+
const serializedDeclarations = parsed.children.toArray();
|
|
2281
|
+
if (serializedDeclarations.length !== 1) return null;
|
|
2282
|
+
const serializedDeclaration = serializedDeclarations[0];
|
|
2283
|
+
if (serializedDeclaration?.type !== "Declaration") return null;
|
|
2284
|
+
if (serializedDeclaration.important) return null;
|
|
2285
|
+
if (css_tree.ident.decode(serializedDeclaration.property) !== name) return null;
|
|
2286
|
+
const valueLocation = serializedDeclaration.value.loc;
|
|
2287
|
+
const safeValue = valueLocation ? serialized.slice(valueLocation.start.offset, valueLocation.end.offset) : css_tree.generate(serializedDeclaration.value);
|
|
2288
|
+
const trimmedObservableValue = canonicalizeBrowserValue(name, canonicalizeLeadingDecimal(observableValue.trim()));
|
|
2289
|
+
const normalizedSafeValue = canonicalizeBrowserValue(name, safeValue.trim());
|
|
2135
2290
|
return {
|
|
2136
2291
|
observableValue: trimmedObservableValue === "0" && zeroLengthProperties.has(name) ? "0px" : trimmedObservableValue,
|
|
2137
|
-
safeValue:
|
|
2292
|
+
safeValue: normalizedSafeValue,
|
|
2138
2293
|
pendingSubstitution
|
|
2139
2294
|
};
|
|
2140
2295
|
} catch {
|
|
@@ -2142,7 +2297,7 @@ function parsePropertyValue(name, observableValue) {
|
|
|
2142
2297
|
}
|
|
2143
2298
|
}
|
|
2144
2299
|
function parseAtruleDescriptorValue(atrule, name, observableValue) {
|
|
2145
|
-
if (
|
|
2300
|
+
if (containsTopLevelDeclarationBoundary(observableValue)) return null;
|
|
2146
2301
|
try {
|
|
2147
2302
|
if (css_tree.lexer.matchAtruleDescriptor(atrule, name, observableValue).error) return null;
|
|
2148
2303
|
const prelude = atrule === "counter-style" ? " sheetom" : "";
|
|
@@ -2223,6 +2378,11 @@ function parseDeclarationValue(declaration, name, observableValue) {
|
|
|
2223
2378
|
if (atrule) return parseAtruleDescriptorValue(atrule, name, observableValue);
|
|
2224
2379
|
return parsePropertyValue(name, observableValue);
|
|
2225
2380
|
}
|
|
2381
|
+
function normalizeDeclarationName(name) {
|
|
2382
|
+
if (name.startsWith("--")) return name;
|
|
2383
|
+
const lowerName = name.toLowerCase();
|
|
2384
|
+
return chromiumPropertyAliases[lowerName] ?? lowerName;
|
|
2385
|
+
}
|
|
2226
2386
|
function namedPropertyToCSS(property) {
|
|
2227
2387
|
if (property === "cssFloat") return "float";
|
|
2228
2388
|
let cssName = property.replace(/[A-Z]/g, (character) => `-${character.toLowerCase()}`);
|
|
@@ -2359,14 +2519,9 @@ var CSSRuleList = class {
|
|
|
2359
2519
|
};
|
|
2360
2520
|
function normalizeMediaText(value) {
|
|
2361
2521
|
try {
|
|
2362
|
-
const
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
});
|
|
2366
|
-
const serialized = decoder.decode(result.code);
|
|
2367
|
-
const blockIndex = serialized.indexOf("{");
|
|
2368
|
-
if (!serialized.startsWith("@media ") || blockIndex === -1) return null;
|
|
2369
|
-
return serialized.slice(7, blockIndex).trim();
|
|
2522
|
+
const parsed = css_tree.parse(value, { context: "mediaQueryList" });
|
|
2523
|
+
if (parsed.type !== "MediaQueryList") return null;
|
|
2524
|
+
return css_tree.generate(parsed).replace(/,\s*/g, ", ").replace(/:\s*/g, ": ").replace(/\s*(<=|>=|=|<|>)\s*/g, " $1 ");
|
|
2370
2525
|
} catch {
|
|
2371
2526
|
return null;
|
|
2372
2527
|
}
|
|
@@ -2644,6 +2799,7 @@ var CSSStyleDeclaration = class {
|
|
|
2644
2799
|
this.parentRule = parentRule;
|
|
2645
2800
|
lockOwnProperties(this, "parentRule");
|
|
2646
2801
|
this.#block = new DeclarationBlock({
|
|
2802
|
+
normalizeName: normalizeDeclarationName,
|
|
2647
2803
|
parseValue: (name, value) => parseDeclarationValue(this, name, value),
|
|
2648
2804
|
shorthandLonghands: getShorthandLonghands,
|
|
2649
2805
|
expandFourSide: expandStaticFourSide,
|
|
@@ -2698,7 +2854,7 @@ var CSSStyleDeclaration = class {
|
|
|
2698
2854
|
for (const child of parsed.children) {
|
|
2699
2855
|
if (child.type !== "Declaration") continue;
|
|
2700
2856
|
declarations.push({
|
|
2701
|
-
name: child.property,
|
|
2857
|
+
name: css_tree.ident.decode(child.property),
|
|
2702
2858
|
value: css_tree.generate(child.value),
|
|
2703
2859
|
important: child.important === true || child.important === "important"
|
|
2704
2860
|
});
|
|
@@ -3443,9 +3599,18 @@ function createRuleFromNodeInternal(node, reportDiagnostic, preserveImports) {
|
|
|
3443
3599
|
rule = new CSSContainerRule(prelude);
|
|
3444
3600
|
break;
|
|
3445
3601
|
case "layer":
|
|
3446
|
-
if (!node.block) return new CSSGenericRule(0,
|
|
3602
|
+
if (!node.block) return new CSSGenericRule(0, `@layer ${prelude.split(",").map((part) => part.trim()).join(", ")};`);
|
|
3447
3603
|
rule = new CSSLayerBlockRule(prelude);
|
|
3448
3604
|
break;
|
|
3605
|
+
case "property": {
|
|
3606
|
+
if (!node.block || !prelude.startsWith("--")) return null;
|
|
3607
|
+
const descriptors = [];
|
|
3608
|
+
for (const child of node.block.children) {
|
|
3609
|
+
if (child.type !== "Declaration") continue;
|
|
3610
|
+
descriptors.push(`${child.property}: ${generateDescriptorInput(child.value)}${child.important ? " !important" : ""};`);
|
|
3611
|
+
}
|
|
3612
|
+
return new CSSGenericRule(genericRuleType(name), `@property ${prelude} {${descriptors.length === 0 ? "" : ` ${descriptors.join(" ")}`} }`);
|
|
3613
|
+
}
|
|
3449
3614
|
case "scope": {
|
|
3450
3615
|
const [start, end] = parseScopePrelude(prelude);
|
|
3451
3616
|
rule = new CSSScopeRule(start, end);
|