@cssdoc/index 0.5.4 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -53,7 +53,7 @@ interface CssValueSites {
53
53
  usages: PropertyUsage[];
54
54
  }
55
55
  /** The kinds of record member a span can be keyed to. */
56
- type MemberKind = "record" | "modifier" | "part" | "shadow-part" | "property" | "function" | "animation" | "layer" | "state" | "condition";
56
+ type MemberKind = "record" | "modifier" | "part" | "shadow-part" | "pseudo-element" | "property" | "function" | "animation" | "layer" | "state" | "condition";
57
57
  /** Build a stable span key for a member. */
58
58
  declare function memberKey(kind: MemberKind, id?: string): string;
59
59
  /** One record in the index: its model entry plus source spans and the facts drift checks need. */
@@ -84,9 +84,20 @@ declare class CssDocIndex {
84
84
  readonly records: readonly RecordInfo[];
85
85
  /** The modifier matcher for this index's convention — how members are matched and rendered. */
86
86
  readonly matcher: ModifierMatcher;
87
+ /** Every custom property's effective value (`--x: value` declarations and `@property` initial-values). */
88
+ readonly customPropertyValues: ReadonlyMap<string, string>;
87
89
  private readonly byName;
88
90
  private readonly byClass;
89
- constructor(records: RecordInfo[], file?: string, matcher?: ModifierMatcher);
91
+ constructor(records: RecordInfo[], file?: string, matcher?: ModifierMatcher, customPropertyValues?: ReadonlyMap<string, string>);
92
+ /**
93
+ * A custom property's declared value and its fully resolved value — following `var()` references
94
+ * through the index to a terminal literal, the way browser dev tools do. `resolved` is omitted when
95
+ * nothing was followed (no `var()`, or the chain couldn't be resolved further).
96
+ */
97
+ resolveCustomProperty(name: string): {
98
+ declared?: string;
99
+ resolved?: string;
100
+ };
90
101
  /** Every documented record's model entry. */
91
102
  get entries(): CssDocEntry[];
92
103
  /** The record whose base class matches `className` (with or without a leading dot). */
package/dist/index.mjs CHANGED
@@ -34,23 +34,66 @@ const spanOf = (node) => {
34
34
  }
35
35
  };
36
36
  };
37
+ /**
38
+ * Follow the `var()` references in `value` through `values` to a terminal literal — the resolution a
39
+ * browser's dev tools show. Uses a `var(--x, fallback)` fallback when `--x` is undefined, and leaves a
40
+ * reference in place on an unknown property or a cycle (so the output degrades to the furthest it could
41
+ * reach rather than looping).
42
+ */
43
+ function resolveValue(value, values, seen) {
44
+ if (!value.includes("var(")) return value;
45
+ const parsed = valueParser(value);
46
+ parsed.walk((node) => {
47
+ if (node.type !== "function" || node.value !== "var") return void 0;
48
+ const ref = node.nodes.find((n) => n.type === "word")?.value;
49
+ if (!ref?.startsWith("--")) return false;
50
+ const comma = node.nodes.findIndex((n) => n.type === "div" && n.value === ",");
51
+ const fallback = comma >= 0 ? valueParser.stringify(node.nodes.slice(comma + 1)).trim() : void 0;
52
+ const declared = seen.has(ref) ? void 0 : values.get(ref);
53
+ const replacement = declared !== void 0 ? resolveValue(declared, values, /* @__PURE__ */ new Set([...seen, ref])) : fallback !== void 0 ? resolveValue(fallback, values, seen) : void 0;
54
+ if (replacement !== void 0) {
55
+ node.type = "word";
56
+ node.value = replacement;
57
+ node.nodes = [];
58
+ }
59
+ return false;
60
+ });
61
+ return valueParser.stringify(parsed.nodes);
62
+ }
37
63
  /** A queryable view over the parsed records. */
38
64
  var CssDocIndex = class {
39
65
  file;
40
66
  records;
41
67
  /** The modifier matcher for this index's convention — how members are matched and rendered. */
42
68
  matcher;
69
+ /** Every custom property's effective value (`--x: value` declarations and `@property` initial-values). */
70
+ customPropertyValues;
43
71
  byName = /* @__PURE__ */ new Map();
44
72
  byClass = /* @__PURE__ */ new Map();
45
- constructor(records, file, matcher) {
73
+ constructor(records, file, matcher, customPropertyValues = /* @__PURE__ */ new Map()) {
46
74
  this.records = records;
47
75
  this.file = file;
48
76
  this.matcher = matcher ?? new ModifierMatcher(DEFAULT_MODIFIER_CONVENTION);
77
+ this.customPropertyValues = customPropertyValues;
49
78
  for (const record of records) {
50
79
  this.byName.set(record.entry.name, record);
51
80
  this.byClass.set(stripDot(record.entry.className), record);
52
81
  }
53
82
  }
83
+ /**
84
+ * A custom property's declared value and its fully resolved value — following `var()` references
85
+ * through the index to a terminal literal, the way browser dev tools do. `resolved` is omitted when
86
+ * nothing was followed (no `var()`, or the chain couldn't be resolved further).
87
+ */
88
+ resolveCustomProperty(name) {
89
+ const declared = this.customPropertyValues.get(name);
90
+ if (declared === void 0) return {};
91
+ const resolved = resolveValue(declared, this.customPropertyValues, /* @__PURE__ */ new Set([name]));
92
+ return {
93
+ declared,
94
+ resolved: resolved === declared ? void 0 : resolved
95
+ };
96
+ }
54
97
  /** Every documented record's model entry. */
55
98
  get entries() {
56
99
  return this.records.map((r) => r.entry);
@@ -159,6 +202,7 @@ function scanNodes(nodes, build, base, matcher) {
159
202
  for (const s of selector.matchAll(/:state\(\s*([\w-]+)\s*\)/gu)) set(memberKey("state", s[1]), node);
160
203
  for (const ps of matcher.pseudoStatesIn(selector)) set(memberKey("state", ps.name), node);
161
204
  for (const sp of selector.matchAll(/::part\(\s*([\w-]+)\s*\)/gu)) set(memberKey("shadow-part", sp[1]), node);
205
+ for (const pe of matcher.pseudoElementsIn(selector)) set(memberKey("pseudo-element", pe.name), node);
162
206
  const bare = selector.replace(/::?[\w-]+(\([^)]*\))?/gu, "");
163
207
  const modNames = /* @__PURE__ */ new Set();
164
208
  for (const mod of matcher.modifiersIn(bare, baseNoDot)) {
@@ -229,14 +273,25 @@ function createIndex(css, options = {}) {
229
273
  }
230
274
  if (current) scanNodes([node], current, current.entry.className, matcher);
231
275
  }
232
- return new CssDocIndex(entries.map((entry) => builds.get(entry.name) ?? {
276
+ const records = entries.map((entry) => builds.get(entry.name) ?? {
233
277
  entry,
234
278
  memberSpans: /* @__PURE__ */ new Map(),
235
279
  authoredModifiers: /* @__PURE__ */ new Set(),
236
280
  authoredParts: /* @__PURE__ */ new Set(),
237
281
  authoredShadowParts: /* @__PURE__ */ new Set(),
238
282
  selectorText: ""
239
- }), options.file, matcher);
283
+ });
284
+ const customPropertyValues = /* @__PURE__ */ new Map();
285
+ root.walkAtRules("property", (at) => {
286
+ const name = at.params.trim();
287
+ at.walkDecls("initial-value", (d) => {
288
+ customPropertyValues.set(name, d.value);
289
+ });
290
+ });
291
+ root.walkDecls((d) => {
292
+ if (d.prop.startsWith("--")) customPropertyValues.set(d.prop, d.value);
293
+ });
294
+ return new CssDocIndex(records, options.file, matcher, customPropertyValues);
240
295
  }
241
296
  /**
242
297
  * Extract custom-property assignments (`--name: value`) and `var(--name, fallback)` references from
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cssdoc/index",
3
- "version": "0.5.4",
3
+ "version": "0.6.1",
4
4
  "description": "A queryable semantic index over the @cssdoc/core model, plus a host-agnostic Usage abstraction and source spans — the shared data layer for cssdoc's linters and language server.",
5
5
  "keywords": [
6
6
  "css",
@@ -30,7 +30,7 @@
30
30
  "dependencies": {
31
31
  "postcss": "^8.5.19",
32
32
  "postcss-value-parser": "^4.2.0",
33
- "@cssdoc/core": "0.5.4"
33
+ "@cssdoc/core": "0.6.1"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/node": "^24.13.3",