@variance-authority/core 0.13.0 → 0.15.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/CHANGELOG.md +458 -0
- package/README.md +13 -10
- package/dist/attribute/component-hash.d.ts +3 -2
- package/dist/attribute/component-hash.js +3 -2
- package/dist/attribute/component-hash.js.map +1 -1
- package/dist/attribute/composition.d.ts +16 -0
- package/dist/attribute/composition.js +4 -6
- package/dist/attribute/composition.js.map +1 -1
- package/dist/attribute/index.d.ts +3 -1
- package/dist/attribute/index.js +2 -1
- package/dist/attribute/index.js.map +1 -1
- package/dist/attribute/region.js +1 -5
- package/dist/attribute/region.js.map +1 -1
- package/dist/attribute/source.d.ts +10 -24
- package/dist/attribute/source.js +4 -38
- package/dist/attribute/source.js.map +1 -1
- package/dist/attribute/subject-pieces.d.ts +37 -0
- package/dist/attribute/subject-pieces.js +122 -0
- package/dist/attribute/subject-pieces.js.map +1 -0
- package/dist/format/capture.d.ts +25 -0
- package/dist/format/capture.js +6 -0
- package/dist/format/capture.js.map +1 -1
- package/dist/format/document.d.ts +2 -2
- package/dist/format/document.js +8 -2
- package/dist/format/document.js.map +1 -1
- package/dist/format/drive.d.ts +191 -0
- package/dist/format/drive.js +343 -0
- package/dist/format/drive.js.map +1 -0
- package/dist/format/environment.d.ts +20 -2
- package/dist/format/environment.js +7 -0
- package/dist/format/environment.js.map +1 -1
- package/dist/format/index.d.ts +6 -3
- package/dist/format/index.js +4 -2
- package/dist/format/index.js.map +1 -1
- package/dist/format/intervention.d.ts +79 -0
- package/dist/format/intervention.js.map +1 -1
- package/dist/format/origin.d.ts +32 -0
- package/dist/format/origin.js +54 -0
- package/dist/format/origin.js.map +1 -0
- package/dist/format/snapshot.d.ts +2 -0
- package/dist/format/snapshot.js.map +1 -1
- package/dist/format/stabilize.d.ts +28 -5
- package/dist/format/stabilize.js +48 -100
- package/dist/format/stabilize.js.map +1 -1
- package/dist/format/waits.d.ts +12 -0
- package/dist/format/waits.js +156 -0
- package/dist/format/waits.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/nearest.d.ts +46 -0
- package/dist/nearest.js +102 -0
- package/dist/nearest.js.map +1 -0
- package/dist/relate/before.d.ts +19 -24
- package/dist/relate/before.js +35 -37
- package/dist/relate/before.js.map +1 -1
- package/dist/relate/beyond.d.ts +59 -0
- package/dist/relate/beyond.js +98 -0
- package/dist/relate/beyond.js.map +1 -0
- package/dist/relate/carried.d.ts +122 -0
- package/dist/relate/carried.js +279 -0
- package/dist/relate/carried.js.map +1 -0
- package/dist/relate/dominate.d.ts +19 -0
- package/dist/relate/dominate.js +77 -0
- package/dist/relate/dominate.js.map +1 -0
- package/dist/relate/index.d.ts +3 -0
- package/dist/relate/index.js +3 -0
- package/dist/relate/index.js.map +1 -1
- package/dist/relate/merkle.js +2 -11
- package/dist/relate/merkle.js.map +1 -1
- package/dist/relate/narrow.d.ts +2 -2
- package/dist/relate/narrow.js +26 -73
- package/dist/relate/narrow.js.map +1 -1
- package/dist/relate/reach.d.ts +1 -1
- package/dist/relate/reach.js +10 -53
- package/dist/relate/reach.js.map +1 -1
- package/dist/relate/records.d.ts +1 -1
- package/dist/relate/records.js.map +1 -1
- package/dist/rules/index.d.ts +3 -3
- package/dist/rules/index.js +3 -3
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/normalize/cascade.d.ts +12 -0
- package/dist/rules/normalize/cascade.js +34 -16
- package/dist/rules/normalize/cascade.js.map +1 -1
- package/dist/rules/normalize/fixture.d.ts +1 -0
- package/dist/rules/normalize/fixture.js +1 -0
- package/dist/rules/normalize/fixture.js.map +1 -1
- package/dist/rules/normalize/index.d.ts +2 -2
- package/dist/rules/normalize/index.js +9 -2
- package/dist/rules/normalize/index.js.map +1 -1
- package/dist/rules/normalize/value.d.ts +13 -2
- package/dist/rules/normalize/value.js +42 -8
- package/dist/rules/normalize/value.js.map +1 -1
- package/dist/rules/normalize/wrapper.js +5 -0
- package/dist/rules/normalize/wrapper.js.map +1 -1
- package/dist/rules/ruleset.d.ts +24 -4
- package/dist/rules/ruleset.js +24 -4
- package/dist/rules/ruleset.js.map +1 -1
- package/dist/segment/index.d.ts +20 -4
- package/dist/segment/index.js +28 -7
- package/dist/segment/index.js.map +1 -1
- package/dist/shard/index.d.ts +11 -0
- package/dist/shard/index.js +11 -0
- package/dist/shard/index.js.map +1 -0
- package/dist/shard/placement.d.ts +117 -0
- package/dist/shard/placement.js +170 -0
- package/dist/shard/placement.js.map +1 -0
- package/package.json +8 -1
|
@@ -73,8 +73,20 @@ export interface ResolveInput {
|
|
|
73
73
|
/** Engine-resolved values. When present these win over anything computed here. */
|
|
74
74
|
readonly computedStyle?: Readonly<Record<string, string>>;
|
|
75
75
|
readonly context: InheritContext;
|
|
76
|
+
/** The page's origin, dropped from the `url()` values it serves. */
|
|
77
|
+
readonly origin?: string;
|
|
76
78
|
}
|
|
77
79
|
export declare function resolveStyle(input: ResolveInput): ResolvedStyle;
|
|
80
|
+
/**
|
|
81
|
+
* The value of `property` that wins among `matchedRules`, picked by the order
|
|
82
|
+
* {@link resolveStyle} applies, with no allowlist and no canonicalization.
|
|
83
|
+
*
|
|
84
|
+
* For a property the snapshot reads as something other than a style: `content`
|
|
85
|
+
* is left off the allowlist because a generated box's words are recorded as its
|
|
86
|
+
* text, so {@link resolveStyle} drops it, and the box still has to know which
|
|
87
|
+
* `content` won before it can say them.
|
|
88
|
+
*/
|
|
89
|
+
export declare function declaredValue(matchedRules: readonly MatchedRule[], property: string): string | undefined;
|
|
78
90
|
interface VariableResolution {
|
|
79
91
|
readonly value: string;
|
|
80
92
|
/** Custom property names consulted, in order of first appearance. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { admits, isCustomProperty } from '../ruleset.js';
|
|
2
|
-
import { canonicalizeValue } from './value.js';
|
|
2
|
+
import { canonicalizeValue, matchingParen } from './value.js';
|
|
3
3
|
import { expandDeclaration } from './shorthand.js';
|
|
4
4
|
/**
|
|
5
5
|
* Cascade resolution — ADR-0003 step 5.
|
|
@@ -78,7 +78,7 @@ export function resolveStyle(input) {
|
|
|
78
78
|
if (isCustomProperty(property))
|
|
79
79
|
continue;
|
|
80
80
|
const resolution = resolveVariables(candidate.value, customProperties);
|
|
81
|
-
style[property] = canonicalizeValue(property, resolution.value);
|
|
81
|
+
style[property] = canonicalizeValue(property, resolution.value, input.origin);
|
|
82
82
|
for (const name of resolution.used) {
|
|
83
83
|
const resolved = customProperties[name];
|
|
84
84
|
if (resolved !== undefined)
|
|
@@ -97,7 +97,7 @@ export function resolveStyle(input) {
|
|
|
97
97
|
for (const [property, value] of Object.entries(input.computedStyle)) {
|
|
98
98
|
if (!admits(property) || isCustomProperty(property))
|
|
99
99
|
continue;
|
|
100
|
-
style[property] = canonicalizeValue(property, value);
|
|
100
|
+
style[property] = canonicalizeValue(property, value, input.origin);
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
// Which token drives each property, resolved once and reused for both this
|
|
@@ -223,6 +223,37 @@ function wins(candidate, incumbent) {
|
|
|
223
223
|
}
|
|
224
224
|
return candidate.order >= incumbent.order;
|
|
225
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* The value of `property` that wins among `matchedRules`, picked by the order
|
|
228
|
+
* {@link resolveStyle} applies, with no allowlist and no canonicalization.
|
|
229
|
+
*
|
|
230
|
+
* For a property the snapshot reads as something other than a style: `content`
|
|
231
|
+
* is left off the allowlist because a generated box's words are recorded as its
|
|
232
|
+
* text, so {@link resolveStyle} drops it, and the box still has to know which
|
|
233
|
+
* `content` won before it can say them.
|
|
234
|
+
*/
|
|
235
|
+
export function declaredValue(matchedRules, property) {
|
|
236
|
+
let winner;
|
|
237
|
+
for (const rule of matchedRules) {
|
|
238
|
+
for (const declaration of rule.declarations) {
|
|
239
|
+
if (declaration.property.trim().toLowerCase() !== property)
|
|
240
|
+
continue;
|
|
241
|
+
const candidate = {
|
|
242
|
+
property,
|
|
243
|
+
value: declaration.value.trim(),
|
|
244
|
+
important: declaration.important,
|
|
245
|
+
inline: false,
|
|
246
|
+
specificity: specificityRank(rule.specificity),
|
|
247
|
+
order: rule.order,
|
|
248
|
+
sheet: rule.sheet,
|
|
249
|
+
selector: rule.selector,
|
|
250
|
+
};
|
|
251
|
+
if (winner === undefined || wins(candidate, winner))
|
|
252
|
+
winner = candidate;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return winner?.value;
|
|
256
|
+
}
|
|
226
257
|
/**
|
|
227
258
|
* Collapse `[id, class, type]` into one comparable number.
|
|
228
259
|
*
|
|
@@ -295,19 +326,6 @@ export function resolveVariables(value, customProperties, depth = 0) {
|
|
|
295
326
|
}
|
|
296
327
|
return { value: result, used };
|
|
297
328
|
}
|
|
298
|
-
function matchingParen(input, openIndex) {
|
|
299
|
-
let depth = 0;
|
|
300
|
-
for (let i = openIndex; i < input.length; i += 1) {
|
|
301
|
-
if (input[i] === '(')
|
|
302
|
-
depth += 1;
|
|
303
|
-
else if (input[i] === ')') {
|
|
304
|
-
depth -= 1;
|
|
305
|
-
if (depth === 0)
|
|
306
|
-
return i;
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
return input.length;
|
|
310
|
-
}
|
|
311
329
|
function topLevelComma(input) {
|
|
312
330
|
let depth = 0;
|
|
313
331
|
for (let i = 0; i < input.length; i += 1) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cascade.js","sourceRoot":"","sources":["../../../src/rules/normalize/cascade.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Declaration, MatchedRule } from '../../format/capture.js';\nimport { admits, isCustomProperty } from '../ruleset.js';\nimport { canonicalizeValue } from './value.js';\nimport { expandDeclaration } from './shorthand.js';\n\n/**\n * Cascade resolution — ADR-0003 step 5.\n *\n * Losing declarations cannot reach a pixel, so they must not reach a hash. A\n * specificity war that changes *which rule wins* without changing the *winning\n * value* is a refactor, and refactors must not invalidate baselines.\n *\n * Under a profile with computed style the engine has already done this, and its\n * answer is authoritative. This code runs anyway, for two reasons: it supplies\n * the values under `declared-only` profiles, and under both profiles it supplies\n * the attribution side-channel — *which rule set this?* — which computed style\n * cannot answer at all.\n */\n\n/**\n * Properties that inherit.\n *\n * Restricted to the allowlist: inheriting a property the snapshot does not\n * record would cost work and change nothing. Without this, a `color` set once on\n * a container would be invisible on every descendant that renders text under a\n * declared-only profile — the most common styling pattern there is.\n */\nexport const INHERITED_PROPERTIES: readonly string[] = [\n 'color', 'font-family', 'font-size', 'font-weight', 'font-style', 'font-variant',\n 'font-stretch', 'line-height', 'letter-spacing', 'word-spacing',\n 'text-align', 'text-indent', 'text-transform', 'white-space', 'word-break',\n 'overflow-wrap', 'visibility', 'direction', 'writing-mode',\n 'caption-side', 'border-collapse', 'border-spacing',\n];\n\nconst INHERITED = new Set(INHERITED_PROPERTIES);\n\n/** Values flowing into a node from its ancestors. */\nexport interface InheritContext {\n /** Resolved values of inheritable properties. */\n readonly inherited: Readonly<Record<string, string>>;\n /** Custom properties in scope. Every custom property inherits. */\n readonly customProperties: Readonly<Record<string, string>>;\n\n /**\n * For each inherited property, the token its value came from.\n *\n * Inheritance would otherwise launder away the attribution: a heading that\n * inherits `color` from a container styled with `var(--brand)` produces a\n * delta with no token, no owner chain of its own, and therefore its own\n * `unattributed` root. One token edit would fan out into a docket entry per\n * inheriting text node — destroying precisely the \"one root, N collateral\"\n * claim the token band exists to make.\n */\n readonly inheritedTokens?: Readonly<Record<string, string>>;\n}\n\nexport const EMPTY_CONTEXT: InheritContext = { inherited: {}, customProperties: {} };\n\nexport interface ResolvedStyle {\n /** Winning, canonicalized values for allowlisted properties. */\n readonly style: Readonly<Record<string, string>>;\n /** Custom properties a winning value resolved through: name → resolved value. */\n readonly tokens: Readonly<Record<string, string>>;\n /** Where each winning declaration came from. Outside the hash by design. */\n readonly origins: Readonly<Record<string, DeclarationOrigin>>;\n /**\n * Which token each property's value came from, including inherited ones.\n *\n * Needed because \"this node uses tokens\" is not specific enough to attribute a\n * delta: a node may resolve `color` through a token while its `padding` comes\n * from a literal, and only the first is collateral of a token edit.\n */\n readonly propertyTokens: Readonly<Record<string, string>>;\n /** Context to pass to this node's children. */\n readonly childContext: InheritContext;\n}\n\nexport interface DeclarationOrigin {\n readonly sheet: string;\n readonly selector: string;\n readonly source?: { readonly file: string; readonly line: number };\n readonly tokenName?: string;\n}\n\ninterface Candidate {\n readonly property: string;\n readonly value: string;\n readonly important: boolean;\n readonly inline: boolean;\n readonly specificity: number;\n readonly order: number;\n readonly sheet: string;\n readonly selector: string;\n readonly source?: { readonly file: string; readonly line: number };\n}\n\nexport interface ResolveInput {\n readonly matchedRules: readonly MatchedRule[];\n readonly inlineStyle?: Readonly<Record<string, string>>;\n /** Engine-resolved values. When present these win over anything computed here. */\n readonly computedStyle?: Readonly<Record<string, string>>;\n readonly context: InheritContext;\n}\n\nexport function resolveStyle(input: ResolveInput): ResolvedStyle {\n const candidates = collectCandidates(input);\n const winners = new Map<string, Candidate>();\n\n for (const candidate of candidates) {\n const incumbent = winners.get(candidate.property);\n if (incumbent === undefined || wins(candidate, incumbent)) {\n winners.set(candidate.property, candidate);\n }\n }\n\n const customProperties: Record<string, string> = { ...input.context.customProperties };\n for (const [property, candidate] of winners) {\n if (isCustomProperty(property)) customProperties[property] = candidate.value.trim();\n }\n\n const style: Record<string, string> = {};\n const tokens: Record<string, string> = {};\n const origins: Record<string, DeclarationOrigin> = {};\n\n // Inherited values are the floor: a node that declares nothing still renders\n // with what flowed down to it, and a snapshot omitting that would report two\n // visually different nodes as identical.\n for (const [property, value] of Object.entries(input.context.inherited)) {\n style[property] = value;\n\n // The token's *own* value, not the property's. Storing the inherited\n // property value here made `tokens` mean two different things depending on\n // whether the node also declared the property: a `<button>` recorded\n // `--va-line-height: 1.4` (correct, from the second loop) while the text\n // node under it recorded `--va-line-height: 19.6px` — the used line-height.\n // The differ reads this map to decide whether a token moved, so a font-size\n // change made an untouched token look like it had moved, and the text node\n // became a second root for a change with one cause. Journal 0006's defect 5\n // in a place its corpus could not reach without a layout engine.\n const token = input.context.inheritedTokens?.[property];\n if (token !== undefined) {\n const resolved = customProperties[token];\n // Falls back to the property value when the token's own value is unknown,\n // which is the whole of a declared-only profile: `:root` is outside the\n // subject, so applicability pruning drops it and no custom property can be\n // resolved. The fallback is a deliberate over-report — a stand-in witness\n // that *something* under this token moved — because an absent entry is\n // indistinguishable from \"the token held\", and that reads as `unchanged`.\n tokens[token] = resolved === undefined ? value : tokenValue(property, resolved, customProperties);\n }\n }\n\n for (const [property, candidate] of winners) {\n if (isCustomProperty(property)) continue;\n\n const resolution = resolveVariables(candidate.value, customProperties);\n style[property] = canonicalizeValue(property, resolution.value);\n\n for (const name of resolution.used) {\n const resolved = customProperties[name];\n if (resolved !== undefined) tokens[name] = tokenValue(property, resolved, customProperties);\n }\n\n origins[property] = {\n sheet: candidate.sheet,\n selector: candidate.selector,\n ...(candidate.source ? { source: candidate.source } : {}),\n ...(resolution.used[0] !== undefined ? { tokenName: resolution.used[0] } : {}),\n };\n }\n\n // The engine's own answer supersedes ours wherever it has one. Ours still\n // produced the origins above, which is the half computed style cannot supply.\n if (input.computedStyle) {\n for (const [property, value] of Object.entries(input.computedStyle)) {\n if (!admits(property) || isCustomProperty(property)) continue;\n style[property] = canonicalizeValue(property, value);\n }\n }\n\n // Which token drives each property, resolved once and reused for both this\n // node's own record and what its children inherit.\n //\n // The precedence matters: a node with its own winning declaration is described\n // by *that* declaration's token, or by no token at all — never by the\n // ancestor's. Falling back to the inherited token when a node declared the\n // property itself makes a rule that *overrode* a token look like collateral of\n // it, which splits one docket root into two.\n const propertyTokens: Record<string, string> = {};\n for (const property of Object.keys(style)) {\n const own = origins[property];\n const token = own !== undefined ? own.tokenName : input.context.inheritedTokens?.[property];\n if (token !== undefined) propertyTokens[property] = token;\n }\n\n const childInherited: Record<string, string> = {};\n const childInheritedTokens: Record<string, string> = {};\n\n for (const [property, value] of Object.entries(style)) {\n if (!INHERITED.has(property)) continue;\n childInherited[property] = value;\n\n // Attribution rides along with the value, so a descendant that merely\n // inherits a token-driven colour is still collateral of that token.\n const token = propertyTokens[property];\n if (token !== undefined) childInheritedTokens[property] = token;\n }\n\n return {\n style,\n tokens,\n origins,\n propertyTokens,\n childContext: {\n inherited: childInherited,\n customProperties,\n inheritedTokens: childInheritedTokens,\n },\n };\n}\n\nfunction collectCandidates(input: ResolveInput): Candidate[] {\n const candidates: Candidate[] = [];\n\n for (const rule of input.matchedRules) {\n for (const declaration of rule.declarations) {\n for (const admitted of admitDeclaration(declaration)) {\n candidates.push({\n ...admitted,\n inline: false,\n specificity: specificityRank(rule.specificity),\n order: rule.order,\n sheet: rule.sheet,\n selector: rule.selector,\n ...(rule.source ? { source: rule.source } : {}),\n });\n }\n }\n }\n\n for (const [property, value] of Object.entries(input.inlineStyle ?? {})) {\n const important = value.trimEnd().toLowerCase().endsWith('!important');\n const bare = important ? value.replace(/!\\s*important\\s*$/i, '').trim() : value;\n\n for (const admitted of admitDeclaration({ property, value: bare, important })) {\n candidates.push({\n ...admitted,\n inline: true,\n specificity: 0,\n order: Number.MAX_SAFE_INTEGER,\n sheet: 'inline',\n selector: '[style]',\n });\n }\n }\n\n return candidates;\n}\n\n/**\n * Expand and project one declaration (ADR-0003 step 4).\n *\n * A shorthand that could not be decomposed with confidence is kept under its own\n * name rather than dropped. Dropping it would remove a real value from the hash.\n */\nfunction admitDeclaration(\n declaration: Declaration,\n): readonly { property: string; value: string; important: boolean }[] {\n const property = declaration.property.trim().toLowerCase();\n\n if (isCustomProperty(declaration.property)) {\n // Custom property names are case-sensitive, unlike every other property.\n return [{\n property: declaration.property.trim(),\n value: declaration.value,\n important: declaration.important,\n }];\n }\n\n const expansion = expandDeclaration(property, declaration.value);\n\n // An unexpandable shorthand is admitted under its own name. It is not on the\n // allowlist — nothing that decomposes ever is — but dropping it would remove a\n // real declared value from the hash, and a missing value is a false\n // `unchanged`. Under a profile with computed style the engine's longhands\n // arrive separately and supersede it, so this only widens the declared-only tier.\n if (!expansion.confident) {\n return [{ property, value: declaration.value, important: declaration.important }];\n }\n\n return expansion.declarations\n .filter((entry) => admits(entry.property))\n .map((entry) => ({\n property: entry.property,\n value: entry.value,\n important: declaration.important,\n }));\n}\n\n/**\n * The cascade order that matters here: importance, then origin, then\n * specificity, then document order.\n *\n * Author-origin only — user and user-agent stylesheets are the engine's business,\n * and under a profile with computed style the engine's answer overrides this\n * anyway.\n */\nfunction wins(candidate: Candidate, incumbent: Candidate): boolean {\n if (candidate.important !== incumbent.important) return candidate.important;\n if (candidate.inline !== incumbent.inline) return candidate.inline;\n if (candidate.specificity !== incumbent.specificity) {\n return candidate.specificity > incumbent.specificity;\n }\n return candidate.order >= incumbent.order;\n}\n\n/**\n * Collapse `[id, class, type]` into one comparable number.\n *\n * The 1 000-per-column base is far above any real selector's count in a column,\n * so this cannot produce the classic \"255 classes outrank an id\" overflow bug.\n */\nfunction specificityRank(specificity: readonly [number, number, number]): number {\n return specificity[0] * 1_000_000 + specificity[1] * 1_000 + specificity[2];\n}\n\ninterface VariableResolution {\n readonly value: string;\n /** Custom property names consulted, in order of first appearance. */\n readonly used: readonly string[];\n}\n\n/**\n * Substitute `var(--name, fallback)` against the in-scope custom properties.\n *\n * The *names* are recorded alongside the resolved value, and that is the point:\n * a name is what lets one design-token edit collapse into a single docket root\n * with counted collateral, instead of hundreds of unrelated colour diffs\n * (spec §5, `token` band). A resolved value alone cannot be grouped.\n */\n/**\n * A custom property's value, resolved through any custom properties it uses.\n *\n * The distinction is not cosmetic and it broke the product's headline claim.\n * `--ks-card-radius: var(--va-radius-md)` is how every real design system is\n * built — semantic tokens aliasing primitive ones — and recording the *declared*\n * value stores the literal string `var(--va-radius-md)` on both sides of a\n * change to `--va-radius-md`. The differ reads this map to decide whether a\n * token moved, sees two identical strings, and concludes it did not. So the root\n * fell through to `component:Card`: **one edit to the radius scale was reported\n * as \"Card changed internally\", once per consuming component**, instead of as\n * one token root with counted collateral.\n *\n * The corpus declared the right answer for `token-radius/card` — its `spec` line\n * reads \"token delta ⇒ token is the root\" — and asserted only the root *count*,\n * which is one either way.\n */\nfunction tokenValue(\n property: string,\n declared: string,\n customProperties: Readonly<Record<string, string>>,\n): string {\n return canonicalizeValue(property, resolveVariables(declared, customProperties).value);\n}\n\nexport function resolveVariables(\n value: string,\n customProperties: Readonly<Record<string, string>>,\n depth = 0,\n): VariableResolution {\n if (!value.includes('var(') || depth > 16) return { value, used: [] };\n\n const used: string[] = [];\n let result = '';\n let index = 0;\n\n while (index < value.length) {\n const start = value.indexOf('var(', index);\n if (start < 0) {\n result += value.slice(index);\n break;\n }\n\n result += value.slice(index, start);\n\n const end = matchingParen(value, start + 3);\n const inner = value.slice(start + 4, end);\n const comma = topLevelComma(inner);\n const name = (comma < 0 ? inner : inner.slice(0, comma)).trim();\n const fallback = comma < 0 ? undefined : inner.slice(comma + 1).trim();\n\n if (!used.includes(name)) used.push(name);\n\n const declared = customProperties[name];\n const substituted = declared ?? fallback ?? '';\n\n // A custom property may itself reference another. The depth cap is a cycle\n // guard: `--a: var(--b); --b: var(--a)` is invalid CSS the engine drops, and\n // recursing on it would hang the collector rather than report anything.\n const nested = resolveVariables(substituted, customProperties, depth + 1);\n result += nested.value;\n for (const nestedName of nested.used) {\n if (!used.includes(nestedName)) used.push(nestedName);\n }\n\n index = end + 1;\n }\n\n return { value: result, used };\n}\n\nfunction matchingParen(input: string, openIndex: number): number {\n let depth = 0;\n for (let i = openIndex; i < input.length; i += 1) {\n if (input[i] === '(') depth += 1;\n else if (input[i] === ')') {\n depth -= 1;\n if (depth === 0) return i;\n }\n }\n return input.length;\n}\n\nfunction topLevelComma(input: string): number {\n let depth = 0;\n for (let i = 0; i < input.length; i += 1) {\n const char = input[i];\n if (char === '(') depth += 1;\n else if (char === ')') depth -= 1;\n else if (char === ',' && depth === 0) return i;\n }\n return -1;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cascade.js","sourceRoot":"","sources":["../../../src/rules/normalize/cascade.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Declaration, MatchedRule } from '../../format/capture.js';\nimport { admits, isCustomProperty } from '../ruleset.js';\nimport { canonicalizeValue, matchingParen } from './value.js';\nimport { expandDeclaration } from './shorthand.js';\n\n/**\n * Cascade resolution — ADR-0003 step 5.\n *\n * Losing declarations cannot reach a pixel, so they must not reach a hash. A\n * specificity war that changes *which rule wins* without changing the *winning\n * value* is a refactor, and refactors must not invalidate baselines.\n *\n * Under a profile with computed style the engine has already done this, and its\n * answer is authoritative. This code runs anyway, for two reasons: it supplies\n * the values under `declared-only` profiles, and under both profiles it supplies\n * the attribution side-channel — *which rule set this?* — which computed style\n * cannot answer at all.\n */\n\n/**\n * Properties that inherit.\n *\n * Restricted to the allowlist: inheriting a property the snapshot does not\n * record would cost work and change nothing. Without this, a `color` set once on\n * a container would be invisible on every descendant that renders text under a\n * declared-only profile — the most common styling pattern there is.\n */\nexport const INHERITED_PROPERTIES: readonly string[] = [\n 'color', 'font-family', 'font-size', 'font-weight', 'font-style', 'font-variant',\n 'font-stretch', 'line-height', 'letter-spacing', 'word-spacing',\n 'text-align', 'text-indent', 'text-transform', 'white-space', 'word-break',\n 'overflow-wrap', 'visibility', 'direction', 'writing-mode',\n 'caption-side', 'border-collapse', 'border-spacing',\n];\n\nconst INHERITED = new Set(INHERITED_PROPERTIES);\n\n/** Values flowing into a node from its ancestors. */\nexport interface InheritContext {\n /** Resolved values of inheritable properties. */\n readonly inherited: Readonly<Record<string, string>>;\n /** Custom properties in scope. Every custom property inherits. */\n readonly customProperties: Readonly<Record<string, string>>;\n\n /**\n * For each inherited property, the token its value came from.\n *\n * Inheritance would otherwise launder away the attribution: a heading that\n * inherits `color` from a container styled with `var(--brand)` produces a\n * delta with no token, no owner chain of its own, and therefore its own\n * `unattributed` root. One token edit would fan out into a docket entry per\n * inheriting text node — destroying precisely the \"one root, N collateral\"\n * claim the token band exists to make.\n */\n readonly inheritedTokens?: Readonly<Record<string, string>>;\n}\n\nexport const EMPTY_CONTEXT: InheritContext = { inherited: {}, customProperties: {} };\n\nexport interface ResolvedStyle {\n /** Winning, canonicalized values for allowlisted properties. */\n readonly style: Readonly<Record<string, string>>;\n /** Custom properties a winning value resolved through: name → resolved value. */\n readonly tokens: Readonly<Record<string, string>>;\n /** Where each winning declaration came from. Outside the hash by design. */\n readonly origins: Readonly<Record<string, DeclarationOrigin>>;\n /**\n * Which token each property's value came from, including inherited ones.\n *\n * Needed because \"this node uses tokens\" is not specific enough to attribute a\n * delta: a node may resolve `color` through a token while its `padding` comes\n * from a literal, and only the first is collateral of a token edit.\n */\n readonly propertyTokens: Readonly<Record<string, string>>;\n /** Context to pass to this node's children. */\n readonly childContext: InheritContext;\n}\n\nexport interface DeclarationOrigin {\n readonly sheet: string;\n readonly selector: string;\n readonly source?: { readonly file: string; readonly line: number };\n readonly tokenName?: string;\n}\n\ninterface Candidate {\n readonly property: string;\n readonly value: string;\n readonly important: boolean;\n readonly inline: boolean;\n readonly specificity: number;\n readonly order: number;\n readonly sheet: string;\n readonly selector: string;\n readonly source?: { readonly file: string; readonly line: number };\n}\n\nexport interface ResolveInput {\n readonly matchedRules: readonly MatchedRule[];\n readonly inlineStyle?: Readonly<Record<string, string>>;\n /** Engine-resolved values. When present these win over anything computed here. */\n readonly computedStyle?: Readonly<Record<string, string>>;\n readonly context: InheritContext;\n /** The page's origin, dropped from the `url()` values it serves. */\n readonly origin?: string;\n}\n\nexport function resolveStyle(input: ResolveInput): ResolvedStyle {\n const candidates = collectCandidates(input);\n const winners = new Map<string, Candidate>();\n\n for (const candidate of candidates) {\n const incumbent = winners.get(candidate.property);\n if (incumbent === undefined || wins(candidate, incumbent)) {\n winners.set(candidate.property, candidate);\n }\n }\n\n const customProperties: Record<string, string> = { ...input.context.customProperties };\n for (const [property, candidate] of winners) {\n if (isCustomProperty(property)) customProperties[property] = candidate.value.trim();\n }\n\n const style: Record<string, string> = {};\n const tokens: Record<string, string> = {};\n const origins: Record<string, DeclarationOrigin> = {};\n\n // Inherited values are the floor: a node that declares nothing still renders\n // with what flowed down to it, and a snapshot omitting that would report two\n // visually different nodes as identical.\n for (const [property, value] of Object.entries(input.context.inherited)) {\n style[property] = value;\n\n // The token's *own* value, not the property's. Storing the inherited\n // property value here made `tokens` mean two different things depending on\n // whether the node also declared the property: a `<button>` recorded\n // `--va-line-height: 1.4` (correct, from the second loop) while the text\n // node under it recorded `--va-line-height: 19.6px` — the used line-height.\n // The differ reads this map to decide whether a token moved, so a font-size\n // change made an untouched token look like it had moved, and the text node\n // became a second root for a change with one cause. Journal 0006's defect 5\n // in a place its corpus could not reach without a layout engine.\n const token = input.context.inheritedTokens?.[property];\n if (token !== undefined) {\n const resolved = customProperties[token];\n // Falls back to the property value when the token's own value is unknown,\n // which is the whole of a declared-only profile: `:root` is outside the\n // subject, so applicability pruning drops it and no custom property can be\n // resolved. The fallback is a deliberate over-report — a stand-in witness\n // that *something* under this token moved — because an absent entry is\n // indistinguishable from \"the token held\", and that reads as `unchanged`.\n tokens[token] = resolved === undefined ? value : tokenValue(property, resolved, customProperties);\n }\n }\n\n for (const [property, candidate] of winners) {\n if (isCustomProperty(property)) continue;\n\n const resolution = resolveVariables(candidate.value, customProperties);\n style[property] = canonicalizeValue(property, resolution.value, input.origin);\n\n for (const name of resolution.used) {\n const resolved = customProperties[name];\n if (resolved !== undefined) tokens[name] = tokenValue(property, resolved, customProperties);\n }\n\n origins[property] = {\n sheet: candidate.sheet,\n selector: candidate.selector,\n ...(candidate.source ? { source: candidate.source } : {}),\n ...(resolution.used[0] !== undefined ? { tokenName: resolution.used[0] } : {}),\n };\n }\n\n // The engine's own answer supersedes ours wherever it has one. Ours still\n // produced the origins above, which is the half computed style cannot supply.\n if (input.computedStyle) {\n for (const [property, value] of Object.entries(input.computedStyle)) {\n if (!admits(property) || isCustomProperty(property)) continue;\n style[property] = canonicalizeValue(property, value, input.origin);\n }\n }\n\n // Which token drives each property, resolved once and reused for both this\n // node's own record and what its children inherit.\n //\n // The precedence matters: a node with its own winning declaration is described\n // by *that* declaration's token, or by no token at all — never by the\n // ancestor's. Falling back to the inherited token when a node declared the\n // property itself makes a rule that *overrode* a token look like collateral of\n // it, which splits one docket root into two.\n const propertyTokens: Record<string, string> = {};\n for (const property of Object.keys(style)) {\n const own = origins[property];\n const token = own !== undefined ? own.tokenName : input.context.inheritedTokens?.[property];\n if (token !== undefined) propertyTokens[property] = token;\n }\n\n const childInherited: Record<string, string> = {};\n const childInheritedTokens: Record<string, string> = {};\n\n for (const [property, value] of Object.entries(style)) {\n if (!INHERITED.has(property)) continue;\n childInherited[property] = value;\n\n // Attribution rides along with the value, so a descendant that merely\n // inherits a token-driven colour is still collateral of that token.\n const token = propertyTokens[property];\n if (token !== undefined) childInheritedTokens[property] = token;\n }\n\n return {\n style,\n tokens,\n origins,\n propertyTokens,\n childContext: {\n inherited: childInherited,\n customProperties,\n inheritedTokens: childInheritedTokens,\n },\n };\n}\n\nfunction collectCandidates(input: ResolveInput): Candidate[] {\n const candidates: Candidate[] = [];\n\n for (const rule of input.matchedRules) {\n for (const declaration of rule.declarations) {\n for (const admitted of admitDeclaration(declaration)) {\n candidates.push({\n ...admitted,\n inline: false,\n specificity: specificityRank(rule.specificity),\n order: rule.order,\n sheet: rule.sheet,\n selector: rule.selector,\n ...(rule.source ? { source: rule.source } : {}),\n });\n }\n }\n }\n\n for (const [property, value] of Object.entries(input.inlineStyle ?? {})) {\n const important = value.trimEnd().toLowerCase().endsWith('!important');\n const bare = important ? value.replace(/!\\s*important\\s*$/i, '').trim() : value;\n\n for (const admitted of admitDeclaration({ property, value: bare, important })) {\n candidates.push({\n ...admitted,\n inline: true,\n specificity: 0,\n order: Number.MAX_SAFE_INTEGER,\n sheet: 'inline',\n selector: '[style]',\n });\n }\n }\n\n return candidates;\n}\n\n/**\n * Expand and project one declaration (ADR-0003 step 4).\n *\n * A shorthand that could not be decomposed with confidence is kept under its own\n * name rather than dropped. Dropping it would remove a real value from the hash.\n */\nfunction admitDeclaration(\n declaration: Declaration,\n): readonly { property: string; value: string; important: boolean }[] {\n const property = declaration.property.trim().toLowerCase();\n\n if (isCustomProperty(declaration.property)) {\n // Custom property names are case-sensitive, unlike every other property.\n return [{\n property: declaration.property.trim(),\n value: declaration.value,\n important: declaration.important,\n }];\n }\n\n const expansion = expandDeclaration(property, declaration.value);\n\n // An unexpandable shorthand is admitted under its own name. It is not on the\n // allowlist — nothing that decomposes ever is — but dropping it would remove a\n // real declared value from the hash, and a missing value is a false\n // `unchanged`. Under a profile with computed style the engine's longhands\n // arrive separately and supersede it, so this only widens the declared-only tier.\n if (!expansion.confident) {\n return [{ property, value: declaration.value, important: declaration.important }];\n }\n\n return expansion.declarations\n .filter((entry) => admits(entry.property))\n .map((entry) => ({\n property: entry.property,\n value: entry.value,\n important: declaration.important,\n }));\n}\n\n/**\n * The cascade order that matters here: importance, then origin, then\n * specificity, then document order.\n *\n * Author-origin only — user and user-agent stylesheets are the engine's business,\n * and under a profile with computed style the engine's answer overrides this\n * anyway.\n */\nfunction wins(candidate: Candidate, incumbent: Candidate): boolean {\n if (candidate.important !== incumbent.important) return candidate.important;\n if (candidate.inline !== incumbent.inline) return candidate.inline;\n if (candidate.specificity !== incumbent.specificity) {\n return candidate.specificity > incumbent.specificity;\n }\n return candidate.order >= incumbent.order;\n}\n\n/**\n * The value of `property` that wins among `matchedRules`, picked by the order\n * {@link resolveStyle} applies, with no allowlist and no canonicalization.\n *\n * For a property the snapshot reads as something other than a style: `content`\n * is left off the allowlist because a generated box's words are recorded as its\n * text, so {@link resolveStyle} drops it, and the box still has to know which\n * `content` won before it can say them.\n */\nexport function declaredValue(matchedRules: readonly MatchedRule[], property: string): string | undefined {\n let winner: Candidate | undefined;\n\n for (const rule of matchedRules) {\n for (const declaration of rule.declarations) {\n if (declaration.property.trim().toLowerCase() !== property) continue;\n const candidate: Candidate = {\n property,\n value: declaration.value.trim(),\n important: declaration.important,\n inline: false,\n specificity: specificityRank(rule.specificity),\n order: rule.order,\n sheet: rule.sheet,\n selector: rule.selector,\n };\n if (winner === undefined || wins(candidate, winner)) winner = candidate;\n }\n }\n\n return winner?.value;\n}\n\n/**\n * Collapse `[id, class, type]` into one comparable number.\n *\n * The 1 000-per-column base is far above any real selector's count in a column,\n * so this cannot produce the classic \"255 classes outrank an id\" overflow bug.\n */\nfunction specificityRank(specificity: readonly [number, number, number]): number {\n return specificity[0] * 1_000_000 + specificity[1] * 1_000 + specificity[2];\n}\n\ninterface VariableResolution {\n readonly value: string;\n /** Custom property names consulted, in order of first appearance. */\n readonly used: readonly string[];\n}\n\n/**\n * Substitute `var(--name, fallback)` against the in-scope custom properties.\n *\n * The *names* are recorded alongside the resolved value, and that is the point:\n * a name is what lets one design-token edit collapse into a single docket root\n * with counted collateral, instead of hundreds of unrelated colour diffs\n * (spec §5, `token` band). A resolved value alone cannot be grouped.\n */\n/**\n * A custom property's value, resolved through any custom properties it uses.\n *\n * The distinction is not cosmetic and it broke the product's headline claim.\n * `--ks-card-radius: var(--va-radius-md)` is how every real design system is\n * built — semantic tokens aliasing primitive ones — and recording the *declared*\n * value stores the literal string `var(--va-radius-md)` on both sides of a\n * change to `--va-radius-md`. The differ reads this map to decide whether a\n * token moved, sees two identical strings, and concludes it did not. So the root\n * fell through to `component:Card`: **one edit to the radius scale was reported\n * as \"Card changed internally\", once per consuming component**, instead of as\n * one token root with counted collateral.\n *\n * The corpus declared the right answer for `token-radius/card` — its `spec` line\n * reads \"token delta ⇒ token is the root\" — and asserted only the root *count*,\n * which is one either way.\n */\nfunction tokenValue(\n property: string,\n declared: string,\n customProperties: Readonly<Record<string, string>>,\n): string {\n return canonicalizeValue(property, resolveVariables(declared, customProperties).value);\n}\n\nexport function resolveVariables(\n value: string,\n customProperties: Readonly<Record<string, string>>,\n depth = 0,\n): VariableResolution {\n if (!value.includes('var(') || depth > 16) return { value, used: [] };\n\n const used: string[] = [];\n let result = '';\n let index = 0;\n\n while (index < value.length) {\n const start = value.indexOf('var(', index);\n if (start < 0) {\n result += value.slice(index);\n break;\n }\n\n result += value.slice(index, start);\n\n const end = matchingParen(value, start + 3);\n const inner = value.slice(start + 4, end);\n const comma = topLevelComma(inner);\n const name = (comma < 0 ? inner : inner.slice(0, comma)).trim();\n const fallback = comma < 0 ? undefined : inner.slice(comma + 1).trim();\n\n if (!used.includes(name)) used.push(name);\n\n const declared = customProperties[name];\n const substituted = declared ?? fallback ?? '';\n\n // A custom property may itself reference another. The depth cap is a cycle\n // guard: `--a: var(--b); --b: var(--a)` is invalid CSS the engine drops, and\n // recursing on it would hang the collector rather than report anything.\n const nested = resolveVariables(substituted, customProperties, depth + 1);\n result += nested.value;\n for (const nestedName of nested.used) {\n if (!used.includes(nestedName)) used.push(nestedName);\n }\n\n index = end + 1;\n }\n\n return { value: result, used };\n}\n\nfunction topLevelComma(input: string): number {\n let depth = 0;\n for (let i = 0; i < input.length; i += 1) {\n const char = input[i];\n if (char === '(') depth += 1;\n else if (char === ')') depth -= 1;\n else if (char === ',' && depth === 0) return i;\n }\n return -1;\n}\n"]}
|
|
@@ -77,6 +77,7 @@ export interface CaptureSpec {
|
|
|
77
77
|
readonly subjectId?: string;
|
|
78
78
|
readonly engine?: string;
|
|
79
79
|
readonly fonts?: readonly string[];
|
|
80
|
+
readonly baseUrl?: string;
|
|
80
81
|
}
|
|
81
82
|
export declare function capture(spec: CaptureSpec): RawCapture;
|
|
82
83
|
export { JSDOM_PROFILE, CHROMIUM_PROFILE };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fixture.js","sourceRoot":"","sources":["../../../src/rules/normalize/fixture.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAA2B,MAAM,yBAAyB,CAAC;AAEnG,OAAO,EAAE,WAAW,EAAwC,MAAM,4BAA4B,CAAC;AA4E/F,MAAM,UAAU,IAAI,CAAC,IAAI,GAAa,EAAE;IACtC,MAAM,IAAI,GACR,IAAI,CAAC,IAAI,KAAK,SAAS;QACvB,IAAI,CAAC,IAAI,KAAK,SAAS;QACvB,IAAI,CAAC,WAAW,KAAK,SAAS;QAC9B,IAAI,CAAC,KAAK,KAAK,SAAS;QACtB,CAAC,CAAC;YACE,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,IAAI;YACvB,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,IAAI;YACvB,GAAG,CAAC,IAAI,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5E,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,EAAE;SACxB;QACH,CAAC,CAAC,SAAS,CAAC;IAEhB,OAAO;QACL,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,KAAK;QACtB,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,EAAE;QACjC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzB,YAAY,EAAE,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;QAC1C,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzC,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClD,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK;YAC1C,CAAC,CAAC;gBACE,UAAU,EAAE;oBACV,MAAM,EAAE,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;wBAC1C,IAAI,EAAE,KAAK,CAAC,IAAI;wBAChB,WAAW,EAAE,WAAW,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;qBAC5C,CAAC,CAAC;oBACH,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC/C,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBAC7C;aACF;YACH,CAAC,CAAC,EAAE,CAAC;QACP,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,EAAE;KAC9B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,IAAc;IACjC,MAAM,YAAY,GAAkB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QAC3F,QAAQ;QACR,KAAK;QACL,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,KAAK;KACnC,CAAC,CAAC,CAAC;IAEJ,OAAO;QACL,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,WAAW;QAChC,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAC1C,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,CAAC;QACtB,YAAY;KACb,CAAC;AACJ,CAAC;
|
|
1
|
+
{"version":3,"file":"fixture.js","sourceRoot":"","sources":["../../../src/rules/normalize/fixture.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n Declaration,\n MatchedRule,\n RawCapture,\n RawNode,\n Rect,\n} from '../../format/capture.js';\nimport { CHROMIUM_PROFILE, JSDOM_PROFILE, type ObservationProfile } from '../../format/profile.js';\nimport type { Holding } from '../../format/holding.js';\nimport { propsDigest, type SourceLocation, type StackFrame } from '../../format/provenance.js';\n\n/**\n * Fixture builders for `RawCapture`.\n *\n * These exist because the collector/core split makes them possible: `core` never\n * touches a DOM, so its entire behavior is testable from hand-written data with\n * no browser, no JSDOM, and no React. That is not a testing convenience — it is\n * the property that lets the same normalizer serve a remote sub-renderer\n * (ADR-0002), and these fixtures are how we keep it honest.\n *\n * Exported rather than test-local so collectors can assert that what they emit\n * round-trips through the same shapes.\n */\n\nexport interface NodeSpec {\n readonly tag?: string;\n readonly attributes?: Record<string, string>;\n readonly role?: string;\n readonly name?: string;\n readonly description?: string;\n readonly state?: Record<string, string | boolean | number>;\n readonly rules?: readonly RuleSpec[];\n readonly inlineStyle?: Record<string, string>;\n readonly computedStyle?: Record<string, string>;\n readonly rect?: Rect;\n readonly text?: string;\n /**\n * Already-built nodes, not specs.\n *\n * Callers write `node({children: [node({...})]})`, so the children arrive\n * built. Re-running the builder over them would read `spec.rules` off a\n * `RawNode` — which has `matchedRules` — and silently produce a child with no\n * style at all. Every style assertion below such a node would then pass by\n * comparing nothing to nothing.\n */\n readonly children?: readonly RawNode[];\n\n /**\n * Owner chain, innermost first, with the props each boundary received.\n * Digests are computed with the real `propsDigest`, so attribution tests\n * exercise the shipped rule rather than a stand-in.\n */\n readonly owners?: readonly OwnerSpec[];\n\n /** Where the JSX that created this node is written, as a runtime records it. */\n readonly source?: SourceLocation;\n\n /** Frames React captured, which a location has not yet been bought with. */\n readonly stack?: readonly StackFrame[];\n\n /**\n * What the boundary rooted here was holding, as a framework adapter read it.\n *\n * Written out rather than derived from a props spec, because the cases worth\n * writing are the ones a real render makes awkward to reach: a hook chain that\n * ran out mid-read, a production build with no hook names at all, and a\n * boundary whose every input agreed. See `compare/parting.ts`.\n */\n readonly holding?: Holding;\n}\n\nexport interface OwnerSpec {\n readonly name: string;\n readonly props?: Record<string, unknown>;\n}\n\nexport interface RuleSpec {\n readonly selector: string;\n readonly declare: Record<string, string>;\n readonly specificity?: readonly [number, number, number];\n readonly order?: number;\n readonly sheet?: string;\n readonly important?: boolean;\n}\n\nexport function node(spec: NodeSpec = {}): RawNode {\n const aria =\n spec.role !== undefined ||\n spec.name !== undefined ||\n spec.description !== undefined ||\n spec.state !== undefined\n ? {\n role: spec.role ?? null,\n name: spec.name ?? null,\n ...(spec.description !== undefined ? { description: spec.description } : {}),\n state: spec.state ?? {},\n }\n : undefined;\n\n return {\n tag: spec.tag ?? 'div',\n attributes: spec.attributes ?? {},\n ...(aria ? { aria } : {}),\n matchedRules: (spec.rules ?? []).map(rule),\n ...(spec.inlineStyle ? { inlineStyle: spec.inlineStyle } : {}),\n ...(spec.computedStyle ? { computedStyle: spec.computedStyle } : {}),\n ...(spec.rect ? { rect: spec.rect } : {}),\n ...(spec.text !== undefined ? { text: spec.text } : {}),\n ...(spec.holding ? { holding: spec.holding } : {}),\n ...(spec.owners || spec.source || spec.stack\n ? {\n provenance: {\n owners: (spec.owners ?? []).map((owner) => ({\n name: owner.name,\n propsDigest: propsDigest(owner.props ?? {}),\n })),\n ...(spec.source ? { source: spec.source } : {}),\n ...(spec.stack ? { stack: spec.stack } : {}),\n },\n }\n : {}),\n children: spec.children ?? [],\n };\n}\n\nexport function rule(spec: RuleSpec): MatchedRule {\n const declarations: Declaration[] = Object.entries(spec.declare).map(([property, value]) => ({\n property,\n value,\n important: spec.important ?? false,\n }));\n\n return {\n sheet: spec.sheet ?? 'sheet.css',\n selector: spec.selector,\n specificity: spec.specificity ?? [0, 1, 0],\n order: spec.order ?? 0,\n declarations,\n };\n}\n\nexport interface CaptureSpec {\n readonly root: RawNode;\n readonly profile?: ObservationProfile;\n readonly inheritedSeed?: Record<string, string>;\n readonly subjectId?: string;\n readonly engine?: string;\n readonly fonts?: readonly string[];\n readonly baseUrl?: string;\n}\n\nexport function capture(spec: CaptureSpec): RawCapture {\n const profile = spec.profile ?? JSDOM_PROFILE;\n\n return {\n captureVersion: 1,\n subject: { id: spec.subjectId ?? 'fixture:subject', kind: 'fixture' },\n profile,\n environment: {\n profile: profile.id,\n engine: spec.engine ?? `${profile.id}@test`,\n viewport: { width: 1280, height: 720, deviceScaleFactor: 1, colorScheme: 'light' },\n fonts: spec.fonts ?? ['Inter/400/normal/deadbeef'],\n conditions: {},\n assets: {},\n },\n root: spec.root,\n inheritedSeed: spec.inheritedSeed ?? {},\n ...(spec.baseUrl !== undefined ? { baseUrl: spec.baseUrl } : {}),\n diagnostics: [],\n };\n}\n\nexport { JSDOM_PROFILE, CHROMIUM_PROFILE };\n"]}
|
|
@@ -3,9 +3,9 @@ import { type Digest } from '../../format/hash.js';
|
|
|
3
3
|
import type { SemanticSnapshot } from '../../format/snapshot.js';
|
|
4
4
|
export { buildAliasMap, aliasAttributeValue, aliasStyleValue } from './alias.js';
|
|
5
5
|
export type { AliasMap, AliasResult } from './alias.js';
|
|
6
|
-
export { resolveStyle, resolveVariables, INHERITED_PROPERTIES, EMPTY_CONTEXT, } from './cascade.js';
|
|
6
|
+
export { declaredValue, resolveStyle, resolveVariables, INHERITED_PROPERTIES, EMPTY_CONTEXT, } from './cascade.js';
|
|
7
7
|
export type { InheritContext, ResolvedStyle, DeclarationOrigin } from './cascade.js';
|
|
8
|
-
export { canonicalizeValue, canonicalizeTokens, canonicalizeDimension } from './value.js';
|
|
8
|
+
export { canonicalizeValue, canonicalizeTokens, canonicalizeDimension, matchingParen } from './value.js';
|
|
9
9
|
export { canonicalizeColor, parseColor, formatColor } from './color.js';
|
|
10
10
|
export { expandDeclaration, SHORTHAND_PROPERTIES } from './shorthand.js';
|
|
11
11
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { environmentKey } from '../../format/environment.js';
|
|
2
2
|
import { digestCombine, digestValue } from '../../format/hash.js';
|
|
3
|
+
import { pageOrigin } from '../../format/origin.js';
|
|
3
4
|
import { relativizeSource } from '../../format/provenance.js';
|
|
4
5
|
import { ALLOWLIST_VERSION, RULESET_VERSION, admitsAttribute } from '../ruleset.js';
|
|
5
6
|
import { aliasAttributeValue, aliasStyleValue, buildAliasMap } from './alias.js';
|
|
@@ -7,8 +8,8 @@ import { sitesIn, structureOf, styleOf } from './project.js';
|
|
|
7
8
|
import { isInertWrapper } from './wrapper.js';
|
|
8
9
|
import { resolveStyle, } from './cascade.js';
|
|
9
10
|
export { buildAliasMap, aliasAttributeValue, aliasStyleValue } from './alias.js';
|
|
10
|
-
export { resolveStyle, resolveVariables, INHERITED_PROPERTIES, EMPTY_CONTEXT, } from './cascade.js';
|
|
11
|
-
export { canonicalizeValue, canonicalizeTokens, canonicalizeDimension } from './value.js';
|
|
11
|
+
export { declaredValue, resolveStyle, resolveVariables, INHERITED_PROPERTIES, EMPTY_CONTEXT, } from './cascade.js';
|
|
12
|
+
export { canonicalizeValue, canonicalizeTokens, canonicalizeDimension, matchingParen } from './value.js';
|
|
12
13
|
export { canonicalizeColor, parseColor, formatColor } from './color.js';
|
|
13
14
|
export { expandDeclaration, SHORTHAND_PROPERTIES } from './shorthand.js';
|
|
14
15
|
export function normalize(capture, options = {}) {
|
|
@@ -44,6 +45,7 @@ export function normalize(capture, options = {}) {
|
|
|
44
45
|
collapseWrappers,
|
|
45
46
|
digestText,
|
|
46
47
|
...(sourceRoot !== undefined ? { sourceRoot } : {}),
|
|
48
|
+
...withOrigin(pageOrigin(capture.baseUrl)),
|
|
47
49
|
styleProvenance,
|
|
48
50
|
declaredBy: new WeakMap(),
|
|
49
51
|
};
|
|
@@ -128,6 +130,7 @@ function normalizeNode(capture, subtreeRoot, aliases, context, path, state) {
|
|
|
128
130
|
...(node.inlineStyle ? { inlineStyle: node.inlineStyle } : {}),
|
|
129
131
|
...(node.computedStyle ? { computedStyle: node.computedStyle } : {}),
|
|
130
132
|
context: inherited,
|
|
133
|
+
...(state.origin !== undefined ? { origin: state.origin } : {}),
|
|
131
134
|
});
|
|
132
135
|
const style = {};
|
|
133
136
|
for (const [property, value] of Object.entries(resolved.style)) {
|
|
@@ -184,6 +187,7 @@ function normalizeNode(capture, subtreeRoot, aliases, context, path, state) {
|
|
|
184
187
|
// they hash by name, so this reaches the snapshot without reaching the
|
|
185
188
|
// identity — which is the whole contract an ignore is under.
|
|
186
189
|
...(node.ignoredBy && node.ignoredBy.length > 0 ? { ignoredBy: node.ignoredBy } : {}),
|
|
190
|
+
...(node.unread !== undefined ? { unread: node.unread } : {}),
|
|
187
191
|
children,
|
|
188
192
|
};
|
|
189
193
|
state.declaredBy.set(built, declaredHere);
|
|
@@ -248,6 +252,9 @@ function normalizeSelector(selector) {
|
|
|
248
252
|
.replace(/\s+/g, ' ')
|
|
249
253
|
.trim();
|
|
250
254
|
}
|
|
255
|
+
function withOrigin(origin) {
|
|
256
|
+
return origin === undefined ? {} : { origin };
|
|
257
|
+
}
|
|
251
258
|
/** The seed minus its custom properties: values that actually render. */
|
|
252
259
|
function renderableOnly(seed) {
|
|
253
260
|
const renderable = {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/rules/normalize/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAe,MAAM,sBAAsB,CAAC;AAC/E,OAAO,EAAE,gBAAgB,EAAmB,MAAM,4BAA4B,CAAC;AAC/E,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEpF,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,aAAa,EAAoB,MAAM,YAAY,CAAC;AACnG,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAEL,YAAY,GAEb,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEjF,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,GACd,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAC1F,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAgCzE,MAAM,UAAU,SAAS,CAAC,OAAmB,EAAE,OAAO,GAAqB,EAAE;IAC3E,MAAM,EAAE,gBAAgB,GAAG,IAAI,EAAE,UAAU,GAAG,KAAK,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;IAE5E,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;IACtC,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACrD,MAAM,WAAW,GAAiB,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAE3D,KAAK,MAAM,EAAE,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QAClC,WAAW,CAAC,IAAI,CAAC;YACf,QAAQ,EAAE,MAAM;YAChB,IAAI,EAAE,uBAAuB;YAC7B,qEAAqE;YACrE,sEAAsE;YACtE,iDAAiD;YACjD,OAAO,EAAE,oBAAoB,EAAE,wCAAwC;SACxE,CAAC,CAAC;IACL,CAAC;IAED,0EAA0E;IAC1E,EAAE;IACF,8EAA8E;IAC9E,4EAA4E;IAC5E,4EAA4E;IAC5E,+EAA+E;IAC/E,2EAA2E;IAC3E,4EAA4E;IAC5E,6CAA6C;IAC7C,MAAM,IAAI,GAAmB;QAC3B,SAAS,EAAE,cAAc,CAAC,OAAO,CAAC,aAAa,CAAC;QAChD,gBAAgB,EAAE,kBAAkB,CAAC,OAAO,CAAC,aAAa,CAAC;KAC5D,CAAC;IAEF,MAAM,eAAe,GAA2B,EAAE,CAAC;IACnD,MAAM,KAAK,GAAc;QACvB,gBAAgB;QAChB,UAAU;QACV,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnD,eAAe;QACf,UAAU,EAAE,IAAI,OAAO,EAAE;KAC1B,CAAC;IACF,MAAM,SAAS,GAAG,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;IAElF,wEAAwE;IACxE,2EAA2E;IAC3E,6EAA6E;IAC7E,2DAA2D;IAC3D,MAAM,IAAI,GACR,OAAO,CAAC,MAAM,KAAK,CAAC;QAClB,CAAC,CAAC,SAAS;QACX,CAAC,CAAC;YACE,GAAG,SAAS;YACZ,QAAQ,EAAE;gBACR,GAAG,SAAS,CAAC,QAAQ;gBACrB,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;oBACjC,GAAG,aAAa,CACd,OAAO,EACP,MAAM,EACN,OAAO,EACP,EAAE,SAAS,EAAE,EAAE,EAAE,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,EAAE,EAC1D,YAAY,KAAK,EAAE,EACnB,KAAK,CACN;oBACD,SAAS,EAAE,IAAa;iBACzB,CAAC,CAAC;aACJ;SACF,CAAC;IAER,MAAM,aAAa,GAAG,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IACrD,MAAM,SAAS,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAErE,yEAAyE;IACzE,+EAA+E;IAC/E,+EAA+E;IAC/E,kDAAkD;IAClD,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElC,MAAM,WAAW,GAAG,cAAc,CAAC;QACjC,GAAG,OAAO,CAAC,WAAW;QACtB,OAAO,EAAE,eAAe;QACxB,SAAS,EAAE,iBAAiB;KAC7B,CAAC,CAAC;IAEH,OAAO;QACL,aAAa,EAAE,CAAC;QAChB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,WAAW;QACX,6EAA6E;QAC7E,8EAA8E;QAC9E,iEAAiE;QACjE,UAAU,EAAE,aAAa,CAAC,QAAQ,EAAE,CAAC,WAAW,CAAC,cAAc,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;QAC3F,aAAa;QACb,SAAS;QACT,IAAI;QACJ,eAAe;QACf,GAAG,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClD,WAAW;KACZ,CAAC;AACJ,CAAC;AAkBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,SAAS,MAAM,CAAC,UAAsB,EAAE,IAAwB;IAC9D,IAAI,IAAI,KAAK,SAAS,IAAI,UAAU,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,UAAU,CAAC;IAE7E,MAAM,MAAM,GAAG,gBAAgB,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACzD,OAAO,MAAM,KAAK,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,UAAU,EAAE,MAAM,EAAE,CAAC;AAC/E,CAAC;AAED,SAAS,aAAa,CACpB,OAAmB,EACnB,WAAoB,EACpB,OAAoB,EACpB,OAAuB,EACvB,IAAY,EACZ,KAAgB;IAEhB,OAAO,KAAK,CAAC,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAEzC,SAAS,KAAK,CAAC,IAAa,EAAE,SAAyB,EAAE,QAAgB;QACvE,MAAM,QAAQ,GAAG,YAAY,CAAC;YAC5B,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9D,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpE,OAAO,EAAE,SAAS;SACnB,CAAC,CAAC;QAEH,MAAM,KAAK,GAA2B,EAAE,CAAC;QACzC,KAAK,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/D,KAAK,CAAC,QAAQ,CAAC,GAAG,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACpD,CAAC;QAED,KAAK,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAClE,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC;gBACzB,IAAI,EAAE,QAAQ;gBACd,QAAQ;gBACR,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,QAAQ,EAAE,iBAAiB,CAAC,MAAM,CAAC,QAAQ,CAAC;gBAC5C,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC5D,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACpD,CAAC,CAAC;QACL,CAAC;QAED,MAAM,UAAU,GAA2B,EAAE,CAAC;QAC9C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAC5D,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC;gBAAE,SAAS;YACrC,UAAU,CAAC,IAAI,CAAC,GAAG,mBAAmB,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QAC/D,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,KAAK,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAEzE,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,EAAE,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;QACxE,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;QAE5D,MAAM,IAAI,GACR,IAAI,CAAC,IAAI,KAAK,SAAS;YACrB,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,KAAK,CAAC,UAAU;gBAChB,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC;gBACxB,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEjC,MAAM,KAAK,GAAiB;YAC1B,IAAI,EAAE,QAAQ;YACd,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE;YAC3B,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpD,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpD,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzE,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3F,UAAU;YACV,KAAK;YACL,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/E,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,MAAM,GAAG,CAAC;gBACjD,CAAC,CAAC,EAAE,WAAW,EAAE,QAAQ,CAAC,cAAc,EAAE;gBAC1C,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnE,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrF,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/C,sEAAsE;YACtE,qEAAqE;YACrE,sDAAsD;YACtD,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAClD,0EAA0E;YAC1E,uEAAuE;YACvE,6DAA6D;YAC7D,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrF,QAAQ;SACT,CAAC;QAEF,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;QAC1C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,SAAS,eAAe,CACtB,IAAa,EACb,YAA4B,EAC5B,QAAgB;QAEhB,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,CAAC;QAClE,MAAM,KAAK,GAAmB,EAAE,CAAC;QAEjC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,QAAQ,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;YAE5E,4EAA4E;YAC5E,2EAA2E;YAC3E,6EAA6E;YAC7E,IAAI,KAAK,CAAC,gBAAgB,IAAI,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;gBACjF,KAAK,MAAM,UAAU,IAAI,SAAS,CAAC,QAAQ,EAAE,CAAC;oBAC5C,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,GAAG,QAAQ,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;gBAChE,CAAC;gBACD,SAAS;YACX,CAAC;YAED,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,IAAkB,EAAE,IAAY;IAC9C,OAAO;QACL,GAAG,IAAI;QACP,IAAI;QACJ,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,IAAI,IAAI,KAAK,EAAE,CAAC,CAAC;KACjF,CAAC;AACJ,CAAC;AAID;;;;;;;;;;;;;GAaG;AACH,SAAS,aAAa,CAAC,IAAY;IACjC,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACnC,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,iBAAiB,CAAC,QAAgB;IACzC,OAAO,QAAQ;SACZ,OAAO,CAAC,qCAAqC,EAAE,cAAc,CAAC;SAC9D,OAAO,CAAC,4BAA4B,EAAE,cAAc,CAAC;SACrD,OAAO,CAAC,wCAAwC,EAAE,eAAe,CAAC;SAClE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,yEAAyE;AACzE,SAAS,cAAc,CAAC,IAAsC;IAC5D,MAAM,UAAU,GAA2B,EAAE,CAAC;IAC9C,KAAK,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACrD,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,UAAU,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC;IAC/D,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAsC;IAChE,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACrD,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC;IAC1D,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import type { Diagnostic, RawCapture, RawNode } from '../../format/capture.js';\nimport { environmentKey } from '../../format/environment.js';\nimport { digestCombine, digestValue, type Digest } from '../../format/hash.js';\nimport { relativizeSource, type Provenance } from '../../format/provenance.js';\nimport { ALLOWLIST_VERSION, RULESET_VERSION, admitsAttribute } from '../ruleset.js';\nimport type { SemanticNode, SemanticSnapshot, StyleProvenanceEntry } from '../../format/snapshot.js';\nimport { aliasAttributeValue, aliasStyleValue, buildAliasMap, type AliasResult } from './alias.js';\nimport { sitesIn, structureOf, styleOf } from './project.js';\nimport { isInertWrapper } from './wrapper.js';\nimport {\n \n resolveStyle,\n type InheritContext,\n} from './cascade.js';\n\nexport { buildAliasMap, aliasAttributeValue, aliasStyleValue } from './alias.js';\nexport type { AliasMap, AliasResult } from './alias.js';\nexport {\n resolveStyle,\n resolveVariables,\n INHERITED_PROPERTIES,\n EMPTY_CONTEXT,\n} from './cascade.js';\nexport type { InheritContext, ResolvedStyle, DeclarationOrigin } from './cascade.js';\nexport { canonicalizeValue, canonicalizeTokens, canonicalizeDimension } from './value.js';\nexport { canonicalizeColor, parseColor, formatColor } from './color.js';\nexport { expandDeclaration, SHORTHAND_PROPERTIES } from './shorthand.js';\n\n/**\n * The normalization pipeline: `RawCapture` in, `SemanticSnapshot` out.\n *\n * There is exactly one of these, and both observation profiles enter it (ADR-0001).\n * That is what makes \"the same rules ran for JSDOM and Chromium\" a fact of the\n * call graph rather than a claim maintained by discipline.\n */\n\nexport interface NormalizeOptions {\n /**\n * Collapse `div`/`span` wrappers that carry no role, no attributes, and no\n * non-inherited styling. Defaults to on — CSS-in-JS and layout libraries emit\n * these by the dozen, and their arrival and departure during a refactor is the\n * single largest source of structural churn.\n */\n readonly collapseWrappers?: boolean;\n\n /** Replace text with its digest. For subjects whose copy is volatile by policy. */\n readonly digestText?: boolean;\n\n /**\n * Absolute directory the recorded JSX source locations are made relative to.\n *\n * The transform writes absolute paths, and a snapshot is a stored artifact\n * compared across machines. Normalizing here rather than at the reader is the\n * same reason everything else is normalized here: one pipeline, both profiles.\n */\n readonly sourceRoot?: string;\n}\n\nexport function normalize(capture: RawCapture, options: NormalizeOptions = {}): SemanticSnapshot {\n const { collapseWrappers = true, digestText = false, sourceRoot } = options;\n\n const portals = capture.portals ?? [];\n const aliases = buildAliasMap(capture.root, portals);\n const diagnostics: Diagnostic[] = [...capture.diagnostics];\n\n for (const id of aliases.dangling) {\n diagnostics.push({\n severity: 'warn',\n code: 'dangling-id-reference',\n // Reported rather than normalized away: an accessible-name reference\n // pointing outside the subject is either a genuine break or a subject\n // boundary drawn too tightly. Both need a human.\n message: `reference to id \"${id}\" resolves outside the subject subtree`,\n });\n }\n\n // Custom properties go into the resolution scope, never into `inherited`.\n //\n // The seed arrives with both mixed together, and injecting the whole thing as\n // inherited style put every ancestor-declared token into the subject root's\n // `style` map as if it were a rendered value. Two consequences, both bad: a\n // token nothing in the subject consumes still changed that subject's hash — so\n // editing any token invalidated every baseline in the repository — and the\n // resulting delta had no owner chain, producing an `unattributed` root on a\n // change that was perfectly well understood.\n const seed: InheritContext = {\n inherited: renderableOnly(capture.inheritedSeed),\n customProperties: customPropertiesOf(capture.inheritedSeed),\n };\n\n const styleProvenance: StyleProvenanceEntry[] = [];\n const state: WalkState = {\n collapseWrappers,\n digestText,\n ...(sourceRoot !== undefined ? { sourceRoot } : {}),\n styleProvenance,\n declaredBy: new WeakMap(),\n };\n const container = normalizeNode(capture, capture.root, aliases, seed, '0', state);\n\n // Portalled subtrees are appended as children of the subject root, each\n // flagged `portalled`. Appending rather than splicing in place keeps their\n // paths stable when DOM children are added or removed, and keeps them out of\n // the wrapper-collapse pass, which reasons about siblings.\n const root: SemanticNode =\n portals.length === 0\n ? container\n : {\n ...container,\n children: [\n ...container.children,\n ...portals.map((portal, index) => ({\n ...normalizeNode(\n capture,\n portal,\n aliases,\n { inherited: {}, customProperties: seed.customProperties },\n `0/portal:${index}`,\n state,\n ),\n portalled: true as const,\n })),\n ],\n };\n\n const structureHash = digestValue(structureOf(root));\n const styleHash = digestValue(styleOf(root, capture.profile.layout));\n\n // Resolved from the *finished* tree rather than during the walk. Wrapper\n // collapse re-paths whole subtrees, so a path recorded on the way down names a\n // node that may not be there on the way out — and an ignore that quietly moved\n // one level up absorbs a sibling nobody excluded.\n const ignoreSites = sitesIn(root);\n\n const environment = environmentKey({\n ...capture.environment,\n ruleset: RULESET_VERSION,\n allowlist: ALLOWLIST_VERSION,\n });\n\n return {\n formatVersion: 1,\n subject: capture.subject,\n profile: capture.profile,\n environment,\n // Keyed on the *semantic* digest, not the full one. A snapshot is a semantic\n // artifact, so binding it to inputs that only rasterization can observe would\n // split one baseline across machines that render it identically.\n renderHash: digestCombine('render', [environment.semanticDigest, structureHash, styleHash]),\n structureHash,\n styleHash,\n root,\n styleProvenance,\n ...(ignoreSites.length > 0 ? { ignoreSites } : {}),\n diagnostics,\n };\n}\n\ninterface WalkState {\n readonly collapseWrappers: boolean;\n readonly digestText: boolean;\n readonly sourceRoot?: string;\n readonly styleProvenance: StyleProvenanceEntry[];\n /**\n * Properties each node declared *itself*, as opposed to inheriting.\n *\n * The wrapper-inertness test needs this distinction and cannot recover it from\n * the finished node: by then an inherited `color` and a declared `color` are\n * the same entry in `style`. Keyed by node identity rather than threaded\n * through the return type, so the recursive shape stays a plain tree walk.\n */\n readonly declaredBy: WeakMap<SemanticNode, ReadonlySet<string>>;\n}\n\n/**\n * Provenance carrying a path this machine can quote back to a repository, and\n * carrying nothing that belongs to the machine it was captured on.\n *\n * The location arrives from the page, where nothing knows what the repository\n * root is; the collector does, and this is the one point every node passes\n * through afterwards.\n *\n * **`stack` rides through, unspent and unrooted.** Frames hold absolute URLs\n * with a dev server's port in them, which would make them machine-specific if\n * anything compared or stored them — and nothing does. `structureOf` and\n * `styleOf` in `packages/core/src/rules/normalize/project.ts` are whitelists\n * and neither admits provenance at all, so a frame changes no digest and\n * disagrees with no baseline; and a snapshot itself never reaches disk, because\n * the history record writes the hashes it folds out of one. What a frame is, is\n * a fetch nobody has asked for yet, kept against the moment a region or a\n * finding names this node — which is where `locateSites` spends it.\n *\n * Rooting a frame here would be the wrong move even so. A frame's URL is what\n * the *fetch* needs, and a repository-relative path is not fetchable.\n */\nfunction rooted(provenance: Provenance, root: string | undefined): Provenance {\n if (root === undefined || provenance.source === undefined) return provenance;\n\n const source = relativizeSource(provenance.source, root);\n return source === provenance.source ? provenance : { ...provenance, source };\n}\n\nfunction normalizeNode(\n capture: RawCapture,\n subtreeRoot: RawNode,\n aliases: AliasResult,\n context: InheritContext,\n path: string,\n state: WalkState,\n): SemanticNode {\n return build(subtreeRoot, context, path);\n\n function build(node: RawNode, inherited: InheritContext, nodePath: string): SemanticNode {\n const resolved = resolveStyle({\n matchedRules: node.matchedRules,\n ...(node.inlineStyle ? { inlineStyle: node.inlineStyle } : {}),\n ...(node.computedStyle ? { computedStyle: node.computedStyle } : {}),\n context: inherited,\n });\n\n const style: Record<string, string> = {};\n for (const [property, value] of Object.entries(resolved.style)) {\n style[property] = aliasStyleValue(aliases, value);\n }\n\n for (const [property, origin] of Object.entries(resolved.origins)) {\n state.styleProvenance.push({\n path: nodePath,\n property,\n sheet: origin.sheet,\n selector: normalizeSelector(origin.selector),\n ...(origin.tokenName ? { tokenName: origin.tokenName } : {}),\n ...(origin.source ? { source: origin.source } : {}),\n });\n }\n\n const attributes: Record<string, string> = {};\n for (const [name, value] of Object.entries(node.attributes)) {\n if (!admitsAttribute(name)) continue;\n attributes[name] = aliasAttributeValue(aliases, name, value);\n }\n\n const rawId = node.attributes['id'];\n const alias = rawId !== undefined ? aliases.local.get(rawId) : undefined;\n\n const children = collectChildren(node, resolved.childContext, nodePath);\n const declaredHere = new Set(Object.keys(resolved.origins));\n\n const text =\n node.text === undefined\n ? undefined\n : state.digestText\n ? digestValue(node.text)\n : normalizeText(node.text);\n\n const built: SemanticNode = {\n path: nodePath,\n tag: node.tag.toLowerCase(),\n ...(alias !== undefined ? { alias } : {}),\n ...(node.aria?.role ? { role: node.aria.role } : {}),\n ...(node.aria?.name ? { name: node.aria.name } : {}),\n ...(node.aria?.description ? { description: node.aria.description } : {}),\n ...(node.aria && Object.keys(node.aria.state).length > 0 ? { state: node.aria.state } : {}),\n attributes,\n style,\n ...(Object.keys(resolved.tokens).length > 0 ? { tokens: resolved.tokens } : {}),\n ...(Object.keys(resolved.propertyTokens).length > 0\n ? { styleTokens: resolved.propertyTokens }\n : {}),\n ...(capture.profile.layout && node.rect ? { rect: node.rect } : {}),\n ...(text !== undefined ? { text } : {}),\n ...(node.provenance ? { provenance: rooted(node.provenance, state.sourceRoot) } : {}),\n ...(node.wiring ? { wiring: node.wiring } : {}),\n // Carried and never projected, like `ignoredBy` below it. This is the\n // only field on a node that holds values rather than shapes, and the\n // reason it may is that nothing downstream hashes it.\n ...(node.holding ? { holding: node.holding } : {}),\n // Carried, never acted on. `structureOf` and `styleOf` project the fields\n // they hash by name, so this reaches the snapshot without reaching the\n // identity — which is the whole contract an ignore is under.\n ...(node.ignoredBy && node.ignoredBy.length > 0 ? { ignoredBy: node.ignoredBy } : {}),\n children,\n };\n\n state.declaredBy.set(built, declaredHere);\n return built;\n }\n\n function collectChildren(\n node: RawNode,\n childContext: InheritContext,\n nodePath: string,\n ): readonly SemanticNode[] {\n const source = [...node.children, ...(node.shadowChildren ?? [])];\n const built: SemanticNode[] = [];\n\n for (const child of source) {\n const candidate = build(child, childContext, `${nodePath}/${built.length}`);\n\n // A collapsed wrapper is replaced by its children in place. Re-pathing them\n // is why collapse must happen during the walk rather than as a later pass:\n // paths must reflect the final tree, or every downstream reference is stale.\n if (state.collapseWrappers && isInertWrapper(child, candidate, state.declaredBy)) {\n for (const grandchild of candidate.children) {\n built.push(repath(grandchild, `${nodePath}/${built.length}`));\n }\n continue;\n }\n\n built.push(candidate);\n }\n\n return built;\n }\n}\n\nfunction repath(node: SemanticNode, path: string): SemanticNode {\n return {\n ...node,\n path,\n children: node.children.map((child, index) => repath(child, `${path}/${index}`)),\n };\n}\n\n\n\n/**\n * Collapse runs of whitespace to a single space, without trimming.\n *\n * Collapsing matches what `white-space: normal` does, so indentation depth is\n * not a hash input. Trimming does not: a *boundary* space between inline\n * elements is rendered, and removing it changes \"a b\" to \"ab\". Since the\n * formatting context is unknowable without layout, the space is kept —\n * over-reporting in a block context, rather than reporting a visible text change\n * as `unchanged`.\n *\n * This costs little in practice because subjects are React-rendered, and JSX\n * already strips whitespace-only lines and newline-adjacent indentation at\n * compile time. Hand-written HTML pays more.\n */\nfunction normalizeText(text: string): string {\n return text.replace(/\\s+/g, ' ');\n}\n\n/**\n * Replace generated segments in selector text with a placeholder.\n *\n * Selectors reach the snapshot only through the attribution side-channel, which\n * is outside the hash — but a selector naming `css-1a2b3c` is still useless in a\n * docket, because the name changes whenever any declaration in its file changes.\n * The placeholder keeps the shape, which is what identifies the rule to a reader.\n */\nfunction normalizeSelector(selector: string): string {\n return selector\n .replace(/\\.(?:css|sc|emotion)-[a-z0-9]{4,}/gi, '.«generated»')\n .replace(/\\.[\\w-]*__[a-z0-9]{5,}\\b/gi, '.«generated»')\n .replace(/\\[data-(?:styled|emotion|v)-[^\\]]*\\]/gi, '[«generated»]')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\n/** The seed minus its custom properties: values that actually render. */\nfunction renderableOnly(seed: Readonly<Record<string, string>>): Record<string, string> {\n const renderable: Record<string, string> = {};\n for (const [property, value] of Object.entries(seed)) {\n if (!property.startsWith('--')) renderable[property] = value;\n }\n return renderable;\n}\n\nfunction customPropertiesOf(seed: Readonly<Record<string, string>>): Record<string, string> {\n const custom: Record<string, string> = {};\n for (const [property, value] of Object.entries(seed)) {\n if (property.startsWith('--')) custom[property] = value;\n }\n return custom;\n}\n\nexport type { Digest };\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/rules/normalize/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAe,MAAM,sBAAsB,CAAC;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAmB,MAAM,4BAA4B,CAAC;AAC/E,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEpF,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,aAAa,EAAoB,MAAM,YAAY,CAAC;AACnG,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAEL,YAAY,GAEb,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEjF,OAAO,EACL,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,GACd,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACzG,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAgCzE,MAAM,UAAU,SAAS,CAAC,OAAmB,EAAE,OAAO,GAAqB,EAAE;IAC3E,MAAM,EAAE,gBAAgB,GAAG,IAAI,EAAE,UAAU,GAAG,KAAK,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;IAE5E,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;IACtC,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACrD,MAAM,WAAW,GAAiB,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAE3D,KAAK,MAAM,EAAE,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QAClC,WAAW,CAAC,IAAI,CAAC;YACf,QAAQ,EAAE,MAAM;YAChB,IAAI,EAAE,uBAAuB;YAC7B,qEAAqE;YACrE,sEAAsE;YACtE,iDAAiD;YACjD,OAAO,EAAE,oBAAoB,EAAE,wCAAwC;SACxE,CAAC,CAAC;IACL,CAAC;IAED,0EAA0E;IAC1E,EAAE;IACF,8EAA8E;IAC9E,4EAA4E;IAC5E,4EAA4E;IAC5E,+EAA+E;IAC/E,2EAA2E;IAC3E,4EAA4E;IAC5E,6CAA6C;IAC7C,MAAM,IAAI,GAAmB;QAC3B,SAAS,EAAE,cAAc,CAAC,OAAO,CAAC,aAAa,CAAC;QAChD,gBAAgB,EAAE,kBAAkB,CAAC,OAAO,CAAC,aAAa,CAAC;KAC5D,CAAC;IAEF,MAAM,eAAe,GAA2B,EAAE,CAAC;IACnD,MAAM,KAAK,GAAc;QACvB,gBAAgB;QAChB,UAAU;QACV,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnD,GAAG,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC1C,eAAe;QACf,UAAU,EAAE,IAAI,OAAO,EAAE;KAC1B,CAAC;IACF,MAAM,SAAS,GAAG,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;IAElF,wEAAwE;IACxE,2EAA2E;IAC3E,6EAA6E;IAC7E,2DAA2D;IAC3D,MAAM,IAAI,GACR,OAAO,CAAC,MAAM,KAAK,CAAC;QAClB,CAAC,CAAC,SAAS;QACX,CAAC,CAAC;YACE,GAAG,SAAS;YACZ,QAAQ,EAAE;gBACR,GAAG,SAAS,CAAC,QAAQ;gBACrB,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;oBACjC,GAAG,aAAa,CACd,OAAO,EACP,MAAM,EACN,OAAO,EACP,EAAE,SAAS,EAAE,EAAE,EAAE,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,EAAE,EAC1D,YAAY,KAAK,EAAE,EACnB,KAAK,CACN;oBACD,SAAS,EAAE,IAAa;iBACzB,CAAC,CAAC;aACJ;SACF,CAAC;IAER,MAAM,aAAa,GAAG,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IACrD,MAAM,SAAS,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAErE,yEAAyE;IACzE,+EAA+E;IAC/E,+EAA+E;IAC/E,kDAAkD;IAClD,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElC,MAAM,WAAW,GAAG,cAAc,CAAC;QACjC,GAAG,OAAO,CAAC,WAAW;QACtB,OAAO,EAAE,eAAe;QACxB,SAAS,EAAE,iBAAiB;KAC7B,CAAC,CAAC;IAEH,OAAO;QACL,aAAa,EAAE,CAAC;QAChB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,WAAW;QACX,6EAA6E;QAC7E,8EAA8E;QAC9E,iEAAiE;QACjE,UAAU,EAAE,aAAa,CAAC,QAAQ,EAAE,CAAC,WAAW,CAAC,cAAc,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;QAC3F,aAAa;QACb,SAAS;QACT,IAAI;QACJ,eAAe;QACf,GAAG,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClD,WAAW;KACZ,CAAC;AACJ,CAAC;AAoBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,SAAS,MAAM,CAAC,UAAsB,EAAE,IAAwB;IAC9D,IAAI,IAAI,KAAK,SAAS,IAAI,UAAU,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,UAAU,CAAC;IAE7E,MAAM,MAAM,GAAG,gBAAgB,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACzD,OAAO,MAAM,KAAK,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,UAAU,EAAE,MAAM,EAAE,CAAC;AAC/E,CAAC;AAED,SAAS,aAAa,CACpB,OAAmB,EACnB,WAAoB,EACpB,OAAoB,EACpB,OAAuB,EACvB,IAAY,EACZ,KAAgB;IAEhB,OAAO,KAAK,CAAC,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAEzC,SAAS,KAAK,CAAC,IAAa,EAAE,SAAyB,EAAE,QAAgB;QACvE,MAAM,QAAQ,GAAG,YAAY,CAAC;YAC5B,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9D,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpE,OAAO,EAAE,SAAS;YAClB,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAChE,CAAC,CAAC;QAEH,MAAM,KAAK,GAA2B,EAAE,CAAC;QACzC,KAAK,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/D,KAAK,CAAC,QAAQ,CAAC,GAAG,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACpD,CAAC;QAED,KAAK,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAClE,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC;gBACzB,IAAI,EAAE,QAAQ;gBACd,QAAQ;gBACR,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,QAAQ,EAAE,iBAAiB,CAAC,MAAM,CAAC,QAAQ,CAAC;gBAC5C,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC5D,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACpD,CAAC,CAAC;QACL,CAAC;QAED,MAAM,UAAU,GAA2B,EAAE,CAAC;QAC9C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAC5D,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC;gBAAE,SAAS;YACrC,UAAU,CAAC,IAAI,CAAC,GAAG,mBAAmB,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QAC/D,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,KAAK,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAEzE,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,EAAE,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;QACxE,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;QAE5D,MAAM,IAAI,GACR,IAAI,CAAC,IAAI,KAAK,SAAS;YACrB,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,KAAK,CAAC,UAAU;gBAChB,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC;gBACxB,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEjC,MAAM,KAAK,GAAiB;YAC1B,IAAI,EAAE,QAAQ;YACd,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE;YAC3B,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpD,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpD,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzE,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3F,UAAU;YACV,KAAK;YACL,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/E,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,MAAM,GAAG,CAAC;gBACjD,CAAC,CAAC,EAAE,WAAW,EAAE,QAAQ,CAAC,cAAc,EAAE;gBAC1C,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnE,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrF,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/C,sEAAsE;YACtE,qEAAqE;YACrE,sDAAsD;YACtD,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAClD,0EAA0E;YAC1E,uEAAuE;YACvE,6DAA6D;YAC7D,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrF,GAAG,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7D,QAAQ;SACT,CAAC;QAEF,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;QAC1C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,SAAS,eAAe,CACtB,IAAa,EACb,YAA4B,EAC5B,QAAgB;QAEhB,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,CAAC;QAClE,MAAM,KAAK,GAAmB,EAAE,CAAC;QAEjC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,QAAQ,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;YAE5E,4EAA4E;YAC5E,2EAA2E;YAC3E,6EAA6E;YAC7E,IAAI,KAAK,CAAC,gBAAgB,IAAI,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;gBACjF,KAAK,MAAM,UAAU,IAAI,SAAS,CAAC,QAAQ,EAAE,CAAC;oBAC5C,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,GAAG,QAAQ,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;gBAChE,CAAC;gBACD,SAAS;YACX,CAAC;YAED,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,IAAkB,EAAE,IAAY;IAC9C,OAAO;QACL,GAAG,IAAI;QACP,IAAI;QACJ,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,IAAI,IAAI,KAAK,EAAE,CAAC,CAAC;KACjF,CAAC;AACJ,CAAC;AAID;;;;;;;;;;;;;GAaG;AACH,SAAS,aAAa,CAAC,IAAY;IACjC,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACnC,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,iBAAiB,CAAC,QAAgB;IACzC,OAAO,QAAQ;SACZ,OAAO,CAAC,qCAAqC,EAAE,cAAc,CAAC;SAC9D,OAAO,CAAC,4BAA4B,EAAE,cAAc,CAAC;SACrD,OAAO,CAAC,wCAAwC,EAAE,eAAe,CAAC;SAClE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,UAAU,CAAC,MAA0B;IAC5C,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;AAChD,CAAC;AAED,yEAAyE;AACzE,SAAS,cAAc,CAAC,IAAsC;IAC5D,MAAM,UAAU,GAA2B,EAAE,CAAC;IAC9C,KAAK,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACrD,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,UAAU,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC;IAC/D,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAsC;IAChE,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACrD,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC;IAC1D,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import type { Diagnostic, RawCapture, RawNode } from '../../format/capture.js';\nimport { environmentKey } from '../../format/environment.js';\nimport { digestCombine, digestValue, type Digest } from '../../format/hash.js';\nimport { pageOrigin } from '../../format/origin.js';\nimport { relativizeSource, type Provenance } from '../../format/provenance.js';\nimport { ALLOWLIST_VERSION, RULESET_VERSION, admitsAttribute } from '../ruleset.js';\nimport type { SemanticNode, SemanticSnapshot, StyleProvenanceEntry } from '../../format/snapshot.js';\nimport { aliasAttributeValue, aliasStyleValue, buildAliasMap, type AliasResult } from './alias.js';\nimport { sitesIn, structureOf, styleOf } from './project.js';\nimport { isInertWrapper } from './wrapper.js';\nimport {\n \n resolveStyle,\n type InheritContext,\n} from './cascade.js';\n\nexport { buildAliasMap, aliasAttributeValue, aliasStyleValue } from './alias.js';\nexport type { AliasMap, AliasResult } from './alias.js';\nexport {\n declaredValue,\n resolveStyle,\n resolveVariables,\n INHERITED_PROPERTIES,\n EMPTY_CONTEXT,\n} from './cascade.js';\nexport type { InheritContext, ResolvedStyle, DeclarationOrigin } from './cascade.js';\nexport { canonicalizeValue, canonicalizeTokens, canonicalizeDimension, matchingParen } from './value.js';\nexport { canonicalizeColor, parseColor, formatColor } from './color.js';\nexport { expandDeclaration, SHORTHAND_PROPERTIES } from './shorthand.js';\n\n/**\n * The normalization pipeline: `RawCapture` in, `SemanticSnapshot` out.\n *\n * There is exactly one of these, and both observation profiles enter it (ADR-0001).\n * That is what makes \"the same rules ran for JSDOM and Chromium\" a fact of the\n * call graph rather than a claim maintained by discipline.\n */\n\nexport interface NormalizeOptions {\n /**\n * Collapse `div`/`span` wrappers that carry no role, no attributes, and no\n * non-inherited styling. Defaults to on — CSS-in-JS and layout libraries emit\n * these by the dozen, and their arrival and departure during a refactor is the\n * single largest source of structural churn.\n */\n readonly collapseWrappers?: boolean;\n\n /** Replace text with its digest. For subjects whose copy is volatile by policy. */\n readonly digestText?: boolean;\n\n /**\n * Absolute directory the recorded JSX source locations are made relative to.\n *\n * The transform writes absolute paths, and a snapshot is a stored artifact\n * compared across machines. Normalizing here rather than at the reader is the\n * same reason everything else is normalized here: one pipeline, both profiles.\n */\n readonly sourceRoot?: string;\n}\n\nexport function normalize(capture: RawCapture, options: NormalizeOptions = {}): SemanticSnapshot {\n const { collapseWrappers = true, digestText = false, sourceRoot } = options;\n\n const portals = capture.portals ?? [];\n const aliases = buildAliasMap(capture.root, portals);\n const diagnostics: Diagnostic[] = [...capture.diagnostics];\n\n for (const id of aliases.dangling) {\n diagnostics.push({\n severity: 'warn',\n code: 'dangling-id-reference',\n // Reported rather than normalized away: an accessible-name reference\n // pointing outside the subject is either a genuine break or a subject\n // boundary drawn too tightly. Both need a human.\n message: `reference to id \"${id}\" resolves outside the subject subtree`,\n });\n }\n\n // Custom properties go into the resolution scope, never into `inherited`.\n //\n // The seed arrives with both mixed together, and injecting the whole thing as\n // inherited style put every ancestor-declared token into the subject root's\n // `style` map as if it were a rendered value. Two consequences, both bad: a\n // token nothing in the subject consumes still changed that subject's hash — so\n // editing any token invalidated every baseline in the repository — and the\n // resulting delta had no owner chain, producing an `unattributed` root on a\n // change that was perfectly well understood.\n const seed: InheritContext = {\n inherited: renderableOnly(capture.inheritedSeed),\n customProperties: customPropertiesOf(capture.inheritedSeed),\n };\n\n const styleProvenance: StyleProvenanceEntry[] = [];\n const state: WalkState = {\n collapseWrappers,\n digestText,\n ...(sourceRoot !== undefined ? { sourceRoot } : {}),\n ...withOrigin(pageOrigin(capture.baseUrl)),\n styleProvenance,\n declaredBy: new WeakMap(),\n };\n const container = normalizeNode(capture, capture.root, aliases, seed, '0', state);\n\n // Portalled subtrees are appended as children of the subject root, each\n // flagged `portalled`. Appending rather than splicing in place keeps their\n // paths stable when DOM children are added or removed, and keeps them out of\n // the wrapper-collapse pass, which reasons about siblings.\n const root: SemanticNode =\n portals.length === 0\n ? container\n : {\n ...container,\n children: [\n ...container.children,\n ...portals.map((portal, index) => ({\n ...normalizeNode(\n capture,\n portal,\n aliases,\n { inherited: {}, customProperties: seed.customProperties },\n `0/portal:${index}`,\n state,\n ),\n portalled: true as const,\n })),\n ],\n };\n\n const structureHash = digestValue(structureOf(root));\n const styleHash = digestValue(styleOf(root, capture.profile.layout));\n\n // Resolved from the *finished* tree rather than during the walk. Wrapper\n // collapse re-paths whole subtrees, so a path recorded on the way down names a\n // node that may not be there on the way out — and an ignore that quietly moved\n // one level up absorbs a sibling nobody excluded.\n const ignoreSites = sitesIn(root);\n\n const environment = environmentKey({\n ...capture.environment,\n ruleset: RULESET_VERSION,\n allowlist: ALLOWLIST_VERSION,\n });\n\n return {\n formatVersion: 1,\n subject: capture.subject,\n profile: capture.profile,\n environment,\n // Keyed on the *semantic* digest, not the full one. A snapshot is a semantic\n // artifact, so binding it to inputs that only rasterization can observe would\n // split one baseline across machines that render it identically.\n renderHash: digestCombine('render', [environment.semanticDigest, structureHash, styleHash]),\n structureHash,\n styleHash,\n root,\n styleProvenance,\n ...(ignoreSites.length > 0 ? { ignoreSites } : {}),\n diagnostics,\n };\n}\n\ninterface WalkState {\n readonly collapseWrappers: boolean;\n readonly digestText: boolean;\n readonly sourceRoot?: string;\n /** The capture's origin, from `baseUrl`; absent for an opaque one. */\n readonly origin?: string;\n readonly styleProvenance: StyleProvenanceEntry[];\n /**\n * Properties each node declared *itself*, as opposed to inheriting.\n *\n * The wrapper-inertness test needs this distinction and cannot recover it from\n * the finished node: by then an inherited `color` and a declared `color` are\n * the same entry in `style`. Keyed by node identity rather than threaded\n * through the return type, so the recursive shape stays a plain tree walk.\n */\n readonly declaredBy: WeakMap<SemanticNode, ReadonlySet<string>>;\n}\n\n/**\n * Provenance carrying a path this machine can quote back to a repository, and\n * carrying nothing that belongs to the machine it was captured on.\n *\n * The location arrives from the page, where nothing knows what the repository\n * root is; the collector does, and this is the one point every node passes\n * through afterwards.\n *\n * **`stack` rides through, unspent and unrooted.** Frames hold absolute URLs\n * with a dev server's port in them, which would make them machine-specific if\n * anything compared or stored them — and nothing does. `structureOf` and\n * `styleOf` in `packages/core/src/rules/normalize/project.ts` are whitelists\n * and neither admits provenance at all, so a frame changes no digest and\n * disagrees with no baseline; and a snapshot itself never reaches disk, because\n * the history record writes the hashes it folds out of one. What a frame is, is\n * a fetch nobody has asked for yet, kept against the moment a region or a\n * finding names this node — which is where `locateSites` spends it.\n *\n * Rooting a frame here would be the wrong move even so. A frame's URL is what\n * the *fetch* needs, and a repository-relative path is not fetchable.\n */\nfunction rooted(provenance: Provenance, root: string | undefined): Provenance {\n if (root === undefined || provenance.source === undefined) return provenance;\n\n const source = relativizeSource(provenance.source, root);\n return source === provenance.source ? provenance : { ...provenance, source };\n}\n\nfunction normalizeNode(\n capture: RawCapture,\n subtreeRoot: RawNode,\n aliases: AliasResult,\n context: InheritContext,\n path: string,\n state: WalkState,\n): SemanticNode {\n return build(subtreeRoot, context, path);\n\n function build(node: RawNode, inherited: InheritContext, nodePath: string): SemanticNode {\n const resolved = resolveStyle({\n matchedRules: node.matchedRules,\n ...(node.inlineStyle ? { inlineStyle: node.inlineStyle } : {}),\n ...(node.computedStyle ? { computedStyle: node.computedStyle } : {}),\n context: inherited,\n ...(state.origin !== undefined ? { origin: state.origin } : {}),\n });\n\n const style: Record<string, string> = {};\n for (const [property, value] of Object.entries(resolved.style)) {\n style[property] = aliasStyleValue(aliases, value);\n }\n\n for (const [property, origin] of Object.entries(resolved.origins)) {\n state.styleProvenance.push({\n path: nodePath,\n property,\n sheet: origin.sheet,\n selector: normalizeSelector(origin.selector),\n ...(origin.tokenName ? { tokenName: origin.tokenName } : {}),\n ...(origin.source ? { source: origin.source } : {}),\n });\n }\n\n const attributes: Record<string, string> = {};\n for (const [name, value] of Object.entries(node.attributes)) {\n if (!admitsAttribute(name)) continue;\n attributes[name] = aliasAttributeValue(aliases, name, value);\n }\n\n const rawId = node.attributes['id'];\n const alias = rawId !== undefined ? aliases.local.get(rawId) : undefined;\n\n const children = collectChildren(node, resolved.childContext, nodePath);\n const declaredHere = new Set(Object.keys(resolved.origins));\n\n const text =\n node.text === undefined\n ? undefined\n : state.digestText\n ? digestValue(node.text)\n : normalizeText(node.text);\n\n const built: SemanticNode = {\n path: nodePath,\n tag: node.tag.toLowerCase(),\n ...(alias !== undefined ? { alias } : {}),\n ...(node.aria?.role ? { role: node.aria.role } : {}),\n ...(node.aria?.name ? { name: node.aria.name } : {}),\n ...(node.aria?.description ? { description: node.aria.description } : {}),\n ...(node.aria && Object.keys(node.aria.state).length > 0 ? { state: node.aria.state } : {}),\n attributes,\n style,\n ...(Object.keys(resolved.tokens).length > 0 ? { tokens: resolved.tokens } : {}),\n ...(Object.keys(resolved.propertyTokens).length > 0\n ? { styleTokens: resolved.propertyTokens }\n : {}),\n ...(capture.profile.layout && node.rect ? { rect: node.rect } : {}),\n ...(text !== undefined ? { text } : {}),\n ...(node.provenance ? { provenance: rooted(node.provenance, state.sourceRoot) } : {}),\n ...(node.wiring ? { wiring: node.wiring } : {}),\n // Carried and never projected, like `ignoredBy` below it. This is the\n // only field on a node that holds values rather than shapes, and the\n // reason it may is that nothing downstream hashes it.\n ...(node.holding ? { holding: node.holding } : {}),\n // Carried, never acted on. `structureOf` and `styleOf` project the fields\n // they hash by name, so this reaches the snapshot without reaching the\n // identity — which is the whole contract an ignore is under.\n ...(node.ignoredBy && node.ignoredBy.length > 0 ? { ignoredBy: node.ignoredBy } : {}),\n ...(node.unread !== undefined ? { unread: node.unread } : {}),\n children,\n };\n\n state.declaredBy.set(built, declaredHere);\n return built;\n }\n\n function collectChildren(\n node: RawNode,\n childContext: InheritContext,\n nodePath: string,\n ): readonly SemanticNode[] {\n const source = [...node.children, ...(node.shadowChildren ?? [])];\n const built: SemanticNode[] = [];\n\n for (const child of source) {\n const candidate = build(child, childContext, `${nodePath}/${built.length}`);\n\n // A collapsed wrapper is replaced by its children in place. Re-pathing them\n // is why collapse must happen during the walk rather than as a later pass:\n // paths must reflect the final tree, or every downstream reference is stale.\n if (state.collapseWrappers && isInertWrapper(child, candidate, state.declaredBy)) {\n for (const grandchild of candidate.children) {\n built.push(repath(grandchild, `${nodePath}/${built.length}`));\n }\n continue;\n }\n\n built.push(candidate);\n }\n\n return built;\n }\n}\n\nfunction repath(node: SemanticNode, path: string): SemanticNode {\n return {\n ...node,\n path,\n children: node.children.map((child, index) => repath(child, `${path}/${index}`)),\n };\n}\n\n\n\n/**\n * Collapse runs of whitespace to a single space, without trimming.\n *\n * Collapsing matches what `white-space: normal` does, so indentation depth is\n * not a hash input. Trimming does not: a *boundary* space between inline\n * elements is rendered, and removing it changes \"a b\" to \"ab\". Since the\n * formatting context is unknowable without layout, the space is kept —\n * over-reporting in a block context, rather than reporting a visible text change\n * as `unchanged`.\n *\n * This costs little in practice because subjects are React-rendered, and JSX\n * already strips whitespace-only lines and newline-adjacent indentation at\n * compile time. Hand-written HTML pays more.\n */\nfunction normalizeText(text: string): string {\n return text.replace(/\\s+/g, ' ');\n}\n\n/**\n * Replace generated segments in selector text with a placeholder.\n *\n * Selectors reach the snapshot only through the attribution side-channel, which\n * is outside the hash — but a selector naming `css-1a2b3c` is still useless in a\n * docket, because the name changes whenever any declaration in its file changes.\n * The placeholder keeps the shape, which is what identifies the rule to a reader.\n */\nfunction normalizeSelector(selector: string): string {\n return selector\n .replace(/\\.(?:css|sc|emotion)-[a-z0-9]{4,}/gi, '.«generated»')\n .replace(/\\.[\\w-]*__[a-z0-9]{5,}\\b/gi, '.«generated»')\n .replace(/\\[data-(?:styled|emotion|v)-[^\\]]*\\]/gi, '[«generated»]')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction withOrigin(origin: string | undefined): { readonly origin?: string } {\n return origin === undefined ? {} : { origin };\n}\n\n/** The seed minus its custom properties: values that actually render. */\nfunction renderableOnly(seed: Readonly<Record<string, string>>): Record<string, string> {\n const renderable: Record<string, string> = {};\n for (const [property, value] of Object.entries(seed)) {\n if (!property.startsWith('--')) renderable[property] = value;\n }\n return renderable;\n}\n\nfunction customPropertiesOf(seed: Readonly<Record<string, string>>): Record<string, string> {\n const custom: Record<string, string> = {};\n for (const [property, value] of Object.entries(seed)) {\n if (property.startsWith('--')) custom[property] = value;\n }\n return custom;\n}\n\nexport type { Digest };\n"]}
|
|
@@ -8,7 +8,12 @@
|
|
|
8
8
|
* size would fabricate precision the tier does not have.
|
|
9
9
|
*/
|
|
10
10
|
export declare const RELATIVE_UNITS: readonly string[];
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* `origin` is the page's own, `https://shop.example` with no trailing slash. A
|
|
13
|
+
* `url()` it serves is written as its path, so the port a build was served on
|
|
14
|
+
* is not part of the value.
|
|
15
|
+
*/
|
|
16
|
+
export declare function canonicalizeValue(property: string, value: string, origin?: string): string;
|
|
12
17
|
/**
|
|
13
18
|
* Rewrite a value token by token.
|
|
14
19
|
*
|
|
@@ -16,8 +21,14 @@ export declare function canonicalizeValue(property: string, value: string): stri
|
|
|
16
21
|
* family name and a resource path are opaque data, and normalizing their case
|
|
17
22
|
* would change what they refer to.
|
|
18
23
|
*/
|
|
19
|
-
export declare function canonicalizeTokens(value: string): string;
|
|
24
|
+
export declare function canonicalizeTokens(value: string, origin?: string): string;
|
|
20
25
|
/** Canonicalize one `<number><unit>` token. */
|
|
21
26
|
export declare function canonicalizeDimension(token: string): string;
|
|
22
27
|
export declare function isRelativeUnit(unit: string): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* The index of the `)` that closes the `(` at `openIndex`, or `input.length`
|
|
30
|
+
* when nothing closes it. A parenthesis inside a quoted string is text, and a
|
|
31
|
+
* backslash escapes the character after it inside one.
|
|
32
|
+
*/
|
|
33
|
+
export declare function matchingParen(input: string, openIndex: number): number;
|
|
23
34
|
//# sourceMappingURL=value.d.ts.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { withoutOrigin } from '../../format/origin.js';
|
|
1
2
|
import { canonicalizeColor, isColorProperty } from './color.js';
|
|
2
3
|
/**
|
|
3
4
|
* Declaration-value canonicalization.
|
|
@@ -45,10 +46,15 @@ export const RELATIVE_UNITS = [
|
|
|
45
46
|
];
|
|
46
47
|
/** Decimal places kept for a length. Absorbs float noise, keeps sub-pixel intent. */
|
|
47
48
|
const LENGTH_PRECISION = 4;
|
|
48
|
-
|
|
49
|
+
/**
|
|
50
|
+
* `origin` is the page's own, `https://shop.example` with no trailing slash. A
|
|
51
|
+
* `url()` it serves is written as its path, so the port a build was served on
|
|
52
|
+
* is not part of the value.
|
|
53
|
+
*/
|
|
54
|
+
export function canonicalizeValue(property, value, origin) {
|
|
49
55
|
if (isColorProperty(property))
|
|
50
56
|
return canonicalizeColor(value);
|
|
51
|
-
return canonicalizeTokens(value);
|
|
57
|
+
return canonicalizeTokens(value, origin);
|
|
52
58
|
}
|
|
53
59
|
/**
|
|
54
60
|
* Rewrite a value token by token.
|
|
@@ -57,7 +63,7 @@ export function canonicalizeValue(property, value) {
|
|
|
57
63
|
* family name and a resource path are opaque data, and normalizing their case
|
|
58
64
|
* would change what they refer to.
|
|
59
65
|
*/
|
|
60
|
-
export function canonicalizeTokens(value) {
|
|
66
|
+
export function canonicalizeTokens(value, origin) {
|
|
61
67
|
const out = [];
|
|
62
68
|
let index = 0;
|
|
63
69
|
const input = value.trim();
|
|
@@ -77,7 +83,7 @@ export function canonicalizeTokens(value) {
|
|
|
77
83
|
}
|
|
78
84
|
if (input.slice(index).toLowerCase().startsWith('url(')) {
|
|
79
85
|
const end = matchingParen(input, index + 3);
|
|
80
|
-
out.push(`url${input.slice(index + 3, end + 1)}
|
|
86
|
+
out.push(sameOriginPath(`url${input.slice(index + 3, end + 1)}`, origin));
|
|
81
87
|
index = end + 1;
|
|
82
88
|
continue;
|
|
83
89
|
}
|
|
@@ -104,6 +110,17 @@ export function canonicalizeTokens(value) {
|
|
|
104
110
|
}
|
|
105
111
|
return out.join('').trim();
|
|
106
112
|
}
|
|
113
|
+
/** One `url(…)` token, with the page's own origin dropped from its payload. */
|
|
114
|
+
function sameOriginPath(token, origin) {
|
|
115
|
+
if (origin === undefined)
|
|
116
|
+
return token;
|
|
117
|
+
const match = /^url\(\s*(["']?)(.*?)\1\s*\)$/s.exec(token);
|
|
118
|
+
const url = match?.[2];
|
|
119
|
+
if (url === undefined)
|
|
120
|
+
return token;
|
|
121
|
+
const path = withoutOrigin(url, origin);
|
|
122
|
+
return path === url ? token : `url(${match[1]}${path}${match[1]})`;
|
|
123
|
+
}
|
|
107
124
|
/** Canonicalize one `<number><unit>` token. */
|
|
108
125
|
export function canonicalizeDimension(token) {
|
|
109
126
|
const match = /^([+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?)([a-z%]*)$/i.exec(token);
|
|
@@ -146,17 +163,34 @@ function closingQuote(input, start) {
|
|
|
146
163
|
}
|
|
147
164
|
return input.length - 1;
|
|
148
165
|
}
|
|
149
|
-
|
|
166
|
+
/**
|
|
167
|
+
* The index of the `)` that closes the `(` at `openIndex`, or `input.length`
|
|
168
|
+
* when nothing closes it. A parenthesis inside a quoted string is text, and a
|
|
169
|
+
* backslash escapes the character after it inside one.
|
|
170
|
+
*/
|
|
171
|
+
export function matchingParen(input, openIndex) {
|
|
150
172
|
let depth = 0;
|
|
173
|
+
let quote;
|
|
151
174
|
for (let i = openIndex; i < input.length; i += 1) {
|
|
152
|
-
|
|
175
|
+
const char = input[i];
|
|
176
|
+
if (quote !== undefined) {
|
|
177
|
+
if (char === '\\')
|
|
178
|
+
i += 1;
|
|
179
|
+
else if (char === quote)
|
|
180
|
+
quote = undefined;
|
|
181
|
+
}
|
|
182
|
+
else if (char === '"' || char === "'") {
|
|
183
|
+
quote = char;
|
|
184
|
+
}
|
|
185
|
+
else if (char === '(') {
|
|
153
186
|
depth += 1;
|
|
154
|
-
|
|
187
|
+
}
|
|
188
|
+
else if (char === ')') {
|
|
155
189
|
depth -= 1;
|
|
156
190
|
if (depth === 0)
|
|
157
191
|
return i;
|
|
158
192
|
}
|
|
159
193
|
}
|
|
160
|
-
return input.length
|
|
194
|
+
return input.length;
|
|
161
195
|
}
|
|
162
196
|
//# sourceMappingURL=value.js.map
|