@cssdoc/core 0.2.0 → 0.3.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
@@ -26,8 +26,30 @@
26
26
  interface ModifierConvention {
27
27
  /** The structural form. An open union — further forms may be added without a breaking change. */
28
28
  structure: "chained" | "suffix" | "attribute";
29
- /** The prefix/delimiter, interpreted per {@link ModifierConvention.structure}. */
30
- separator: string;
29
+ /**
30
+ * The prefix/delimiter, interpreted per {@link ModifierConvention.structure}. May be a single
31
+ * string or several — any one of which marks a modifier. Separators are matched literally.
32
+ */
33
+ separator: string | string[];
34
+ /**
35
+ * The delimiter that marks a **BEM-style element** inside the base class name (e.g. `"__"` in
36
+ * `.block__element`). When set (`suffix` structure only), a matching class is recorded as a
37
+ * {@link https://cssdoc.dev part} rather than a modifier. Its own modifiers
38
+ * (`.block__element--mod`) are captured as part of the element name for now.
39
+ */
40
+ elementSeparator?: string | string[];
41
+ /**
42
+ * Class prefixes that mark a **state** rather than a modifier (e.g. `["is-", "has-"]`). A class
43
+ * chained to the base whose name starts with one of these is recorded as a state, and is never
44
+ * treated as a modifier. Opt-in; no preset sets it by default.
45
+ */
46
+ statePrefixes?: string[];
47
+ /**
48
+ * Native pseudo-classes (without the `:`) to recognize as states when they appear on the base,
49
+ * e.g. `["disabled", "checked"]`. Defaults to {@link DEFAULT_STATE_PSEUDO_CLASSES} — a curated set
50
+ * of form/UI states, deliberately excluding ubiquitous interaction pseudos (`:hover`, `:focus`).
51
+ */
52
+ statePseudoClasses?: string[];
31
53
  /**
32
54
  * Split the modifier body into `prop`/`value` on {@link ModifierConvention.propValueSeparator}?
33
55
  * Defaults to `false`. Ignored for `attribute` (which always derives `prop` from the attribute name
@@ -37,11 +59,18 @@ interface ModifierConvention {
37
59
  /** The separator for the `prop`/`value` split. Defaults to `-`. */
38
60
  propValueSeparator?: string;
39
61
  }
62
+ /**
63
+ * The pseudo-classes cssdoc treats as component states by default — form and UI states a component
64
+ * meaningfully declares. Ubiquitous interaction pseudos (`:hover`, `:focus`, `:active`) are omitted so
65
+ * incidental rules don't become documented states; add them via `statePseudoClasses` if you want them.
66
+ */
67
+ declare const DEFAULT_STATE_PSEUDO_CLASSES: readonly string[];
40
68
  /** The built-in convention presets. Other schemes use the custom object (see the docs). */
41
69
  declare const MODIFIER_PRESETS: {
42
- /** BEM / SUIT — `.button--primary`. The default. */readonly bem: {
70
+ /** BEM / SUIT — `.button--primary`, with `.button__element` sub-elements. The default. */readonly bem: {
43
71
  readonly structure: "suffix";
44
72
  readonly separator: "--";
73
+ readonly elementSeparator: "__";
45
74
  readonly propValue: false;
46
75
  }; /** rscss — `.button.-color-secondary`, split into `prop`/`value`. */
47
76
  readonly rscss: {
@@ -84,13 +113,60 @@ interface ModifierHit {
84
113
  */
85
114
  declare class ModifierMatcher {
86
115
  readonly convention: ModifierConvention;
87
- private readonly sepEsc;
116
+ /** The separator(s), longest-first so overlapping prefixes (e.g. `--` before `-`) match greedily. */
117
+ private readonly separators;
118
+ /** The separators as a non-capturing regex alternation, e.g. `(?:is-|has-)` (or `(?:)` when empty). */
119
+ private readonly sepAlt;
120
+ /** BEM-style element separators (longest-first), or empty when the convention has none. */
121
+ private readonly elementSeparators;
122
+ /** The element separators as a non-capturing alternation, or `""` when there are none. */
123
+ private readonly elementSepAlt;
124
+ /** State-class prefixes (longest-first), or empty when the convention has none. */
125
+ private readonly statePrefixes;
126
+ /** Native pseudo-classes (no `:`) recognized as states. */
127
+ private readonly statePseudoClasses;
88
128
  constructor(convention: ModifierConvention);
129
+ /** Does a class name (no leading dot) start with one of the convention's state prefixes? */
130
+ private isStateClass;
131
+ /** Strip a leading chained-class separator from `name` (the longest that matches), else return it. */
132
+ private stripPrefix;
133
+ /** Return the suffix body — the part after the first (longest) non-empty separator occurrence. */
134
+ private stripToBody;
89
135
  /**
90
136
  * Every modifier attached to `baseNoDot` within one selector. `selector` should have its pseudos
91
137
  * already dropped (as `parse.ts`/`index.ts` do); the base is given without its leading dot.
92
138
  */
93
139
  modifiersIn(selector: string, baseNoDot: string): ModifierHit[];
140
+ /**
141
+ * Every BEM-style element attached to `baseNoDot` within one selector (`.base<elementSep><name>`),
142
+ * as parts, each with any element-scoped modifiers (`.base__element--mod` → element `base__element`
143
+ * with modifier `mod`). Only meaningful for `suffix` conventions with an `elementSeparator`.
144
+ */
145
+ elementsIn(selector: string, baseNoDot: string): {
146
+ name: string;
147
+ modifiers: ModifierHit[];
148
+ }[];
149
+ /**
150
+ * Split `.base__element--mod`-style tokens into the element class (`base__element`) and, if a
151
+ * modifier separator follows the element name, the element-scoped modifier.
152
+ */
153
+ private splitElementModifier;
154
+ /**
155
+ * Every state class chained to `baseNoDot` within one selector — a class whose name starts with one
156
+ * of the convention's {@link ModifierConvention.statePrefixes} (e.g. `.tabs.is-open` → `is-open`).
157
+ * Empty when the convention sets no state prefixes.
158
+ */
159
+ statesIn(selector: string, baseNoDot: string): {
160
+ name: string;
161
+ }[];
162
+ /**
163
+ * Every native pseudo-class on the selector recognized as a state — a `:name` whose `name` is in the
164
+ * convention's {@link ModifierConvention.statePseudoClasses} (e.g. `.tab:disabled` → `disabled`).
165
+ * Pseudo-elements (`::part`) and pseudos not in the set are ignored.
166
+ */
167
+ pseudoStatesIn(selector: string): {
168
+ name: string;
169
+ }[];
94
170
  /** Derive `prop`/`value` for a modifier `name` (as returned by {@link modifiersIn} or authored). */
95
171
  analyze(name: string): {
96
172
  prop: string;
@@ -105,9 +181,15 @@ declare class ModifierMatcher {
105
181
  * look like a modifier usage of `baseNoDot`? Replaces the old `startsWith("-")` gate.
106
182
  */
107
183
  looksLikeUsage(token: string, baseNoDot?: string): boolean;
184
+ /**
185
+ * Classify a host-document class token relative to `baseNoDot`: a `modifier` usage, a `state` class
186
+ * (a `statePrefixes` prefix), a BEM `element` class (`base<elementSep>…`), or `undefined` if it's
187
+ * none of those. Consumer-side linting routes each kind to the right "unknown-…" check.
188
+ */
189
+ usageKind(token: string, baseNoDot?: string): "modifier" | "state" | "element" | undefined;
108
190
  /** Canonicalize an attribute expression (bracket-inner): normalize quotes to double, trim. */
109
191
  private normalizeAttribute;
110
- /** Does an attribute-expression's name carry the convention's required prefix? */
192
+ /** Does an attribute-expression's name carry one of the convention's required prefixes? */
111
193
  private attributeMatches;
112
194
  }
113
195
  //#endregion
@@ -191,9 +273,9 @@ declare class CssDocConfiguration {
191
273
  setSupportForTags(definitions: readonly CssDocTagDefinition[], supported: boolean): void;
192
274
  /** Disable support for every standard tag (custom tags added later remain supported). */
193
275
  setNoStandardTags(): void;
194
- /** The names (without `@`) of every standard tag. */
276
+ /** The names (without `@`) of every standard tag — the canonical vocabulary from `@cssdoc/spec`. */
195
277
  static readonly standardTagNames: readonly string[];
196
- /** A fresh set of the standard tag definitions (new instances on each call). */
278
+ /** A fresh set of the standard tag definitions (new instances on each call), built from the spec. */
197
279
  static standardTags(): CssDocTagDefinition[];
198
280
  }
199
281
  //#endregion
@@ -242,13 +324,23 @@ interface CssModifier {
242
324
  interface CssPart {
243
325
  /** The part class without the leading dot, e.g. `item`. */
244
326
  name: string;
245
- /** Prose from a `@part` / `@csspart` doc tag, when authored. */
327
+ /** Prose from a `@part` doc tag, when authored. */
246
328
  description?: string;
329
+ /** The part's own modifiers, e.g. `.block__element--active` on a BEM element. Present when non-empty. */
330
+ modifiers?: CssModifier[];
247
331
  }
248
- /** A component state — a `:state()` / state-class the component reacts to (`@cssstate`). */
332
+ /**
333
+ * How a component state is spelled — the CSSOM custom state `:state(x)`, a native pseudo-class
334
+ * (`:disabled`), or a state class from the convention's `statePrefixes` (`.is-open`). Only `custom`
335
+ * maps to a Custom Elements Manifest `cssStates` entry.
336
+ */
337
+ type CssStateKind = "custom" | "pseudo-class" | "class";
338
+ /** A component state — from `:state()`, a native pseudo-class, or a state class (`@cssstate`). */
249
339
  interface CssState {
250
- /** The state name, e.g. `open` or `selected`. */
340
+ /** The state name without its punctuation, e.g. `open`, `selected`, or `disabled`. */
251
341
  name: string;
342
+ /** How the state is expressed in CSS. */
343
+ kind: CssStateKind;
252
344
  /** Prose from a `@cssstate` doc tag, when authored. */
253
345
  description?: string;
254
346
  }
@@ -321,13 +413,14 @@ type CssRecordKind = "component" | "utility" | "rule" | "declaration";
321
413
  */
322
414
  type CssReleaseStage = "alpha" | "beta" | "experimental" | "internal" | "public";
323
415
  /**
324
- * A node in an authored HTML-structure tree (`@structure`): a selector for the element and its
325
- * children. Emitters render it as an indented tree and, via {@link toMermaid}, as a diagram.
416
+ * A node in an authored structure tree (`@structure`), written as nested CSS: a compound selector for
417
+ * the element and its children (the rules nested inside it). Emitters render the tree and, via
418
+ * {@link toMermaid}, a diagram.
326
419
  */
327
420
  interface StructureNode {
328
- /** The node's selector/label, e.g. `.tabs` or `.tab.-selected` or `button`. */
421
+ /** The node's compound selector, e.g. `.tabs`, `.tab.-selected`, or `.list:has(.tab)`. */
329
422
  selector: string;
330
- /** Child nodes (one indentation level deeper). */
423
+ /** Child nodes (rules nested one brace level deeper). */
331
424
  children: StructureNode[];
332
425
  }
333
426
  /** One documented CSS record: its base class plus everything derived from the CSS + doc comments. */
@@ -354,9 +447,11 @@ interface CssDocEntry {
354
447
  accessibility?: string;
355
448
  /** AST-extracted modifiers, annotated with `@modifier` prose where authored. */
356
449
  modifiers: CssModifier[];
357
- /** AST-extracted sub-element parts, annotated with `@part`/`@csspart` prose where authored. */
450
+ /** AST-extracted sub-element parts (class-based), annotated with `@part` prose where authored. */
358
451
  parts: CssPart[];
359
- /** States the component reacts to, from `@cssstate`. */
452
+ /** Shadow-DOM exposed parts (`::part(name)`), from `@csspart` or a `::part()` selector. */
453
+ shadowParts: CssPart[];
454
+ /** States the component reacts to, from `@cssstate`, `:state()`, pseudo-classes, or state classes. */
360
455
  states: CssState[];
361
456
  /** Named slots the component shell exposes, from `@slot`. */
362
457
  slots: CssSlot[];
@@ -374,8 +469,10 @@ interface CssDocEntry {
374
469
  conditions: CssCondition[];
375
470
  /** `@example` blocks, verbatim. */
376
471
  examples: string[];
377
- /** The authored `@structure` HTML tree (top-level nodes), when present. */
472
+ /** The authored `@structure` element tree (top-level nodes), when present. */
378
473
  structure?: StructureNode[];
474
+ /** An optional prose description leading the `@structure` body, when authored. */
475
+ structureDescription?: string;
379
476
  /** `@demo <spec>` (e.g. `self:button`), when authored. */
380
477
  demo?: string;
381
478
  /** Component-level deprecation replacement text, when authored (the argument to a `@deprecated` tag). */
@@ -388,6 +485,8 @@ interface CssDocEntry {
388
485
  */
389
486
  customBlocks?: Record<string, string[]>;
390
487
  }
488
+ /** A PostCSS parse function — turns a source string into a Root. Inject one to read a non-CSS dialect. */
489
+ type CssParse = (css: string) => import("postcss").Root;
391
490
  /** Options for {@link parseCssDocs}. */
392
491
  interface ParseOptions {
393
492
  /**
@@ -404,10 +503,15 @@ interface ParseOptions {
404
503
  configuration?: CssDocConfiguration;
405
504
  /**
406
505
  * The modifier convention — how modifier classes are spelled (BEM `.button--primary` by default;
407
- * `rscss`, `bare`, or a custom {@link import("./modifier.ts").ModifierConvention} for SUIT/CUBE/etc.).
408
- * Overrides the `configuration`'s convention when both are given.
506
+ * `rscss`, `bare`, or a custom `ModifierConvention` for SUIT/CUBE/etc.). Overrides the
507
+ * `configuration`'s convention when both are given.
409
508
  */
410
509
  modifierConvention?: ModifierConventionInput;
510
+ /**
511
+ * The PostCSS parser to read `css` with. Defaults to `postcss.parse` (plain CSS). Inject a dialect
512
+ * parser (e.g. `postcss-scss`/`postcss-less` via `@cssdoc/dialects`) to document `.scss`/`.less`.
513
+ */
514
+ parse?: CssParse;
411
515
  }
412
516
  //#endregion
413
517
  //#region src/parse.d.ts
@@ -488,8 +592,10 @@ interface ParsedDoc {
488
592
  releaseStage?: CssReleaseStage;
489
593
  /** `@modifier` prose, keyed by the modifier class without its dot (e.g. `-color-secondary`). */
490
594
  modifiers: Map<string, DocModifier>;
491
- /** `@part`/`@csspart` descriptions, keyed by the part name without its dot (e.g. `item`). */
595
+ /** `@part` descriptions, keyed by the class part name without its dot (e.g. `item`). */
492
596
  parts: Map<string, string>;
597
+ /** `@csspart` descriptions (shadow-DOM `::part()`), keyed by the bare part name (e.g. `header`). */
598
+ cssParts: Map<string, string>;
493
599
  /** `@cssproperty` declarations. */
494
600
  cssProperties: DocCssProperty[];
495
601
  /** `@cssstate` descriptions, keyed by state name. */
@@ -506,8 +612,10 @@ interface ParsedDoc {
506
612
  conditions: DocCondition[];
507
613
  /** `@example` blocks. */
508
614
  examples: string[];
509
- /** `@structure` — the raw (indented) HTML-tree body, parsed into nodes by {@link parseStructure}. */
615
+ /** `@structure` — the nested-CSS body, parsed into a selector tree by {@link parseStructure}. */
510
616
  structure?: StructureNode[];
617
+ /** An optional prose description leading the `@structure` body. */
618
+ structureDescription?: string;
511
619
  /** The `<replacement>` argument from a `@deprecated` tag. */
512
620
  deprecated?: string;
513
621
  /** `@demo <spec>`. */
@@ -539,16 +647,18 @@ declare function parseDocComment(raw: string, configuration?: CssDocConfiguratio
539
647
  */
540
648
  declare function recordNameOf(commentText: string, configuration?: CssDocConfiguration): string | undefined;
541
649
  /**
542
- * Parse a `@structure` body — an indentation-nested list of element selectors — into a
543
- * {@link StructureNode} tree. Indentation depth (any consistent width) sets nesting; blank lines are
544
- * ignored.
650
+ * Parse a `@structure` body — nested CSS (brace-delimited rules) — into a {@link StructureNode} tree.
651
+ * Each rule's selector becomes a node; nested rules become its children. Because a node is a real
652
+ * compound selector, `:has()` (contains), `:is()` / selector-lists (one-of), and `:not()` (not) express
653
+ * relationships natively. Leaf nodes are written as empty rules (`.tab {}`). A malformed body parses to
654
+ * an empty tree rather than throwing.
545
655
  *
546
656
  * @example
547
657
  * ```
548
- * .tabs
549
- * .list
550
- * .tab
551
- * .panel
658
+ * .tabs {
659
+ * .list { .tab {} }
660
+ * .panel {}
661
+ * }
552
662
  * ```
553
663
  */
554
664
  declare function parseStructure(raw: string): StructureNode[];
@@ -585,4 +695,4 @@ declare function toMermaid(roots: StructureNode[], options?: {
585
695
  */
586
696
  declare function toJson(model: CssDocEntry[]): string;
587
697
  //#endregion
588
- export { type CssAnimation, type CssCondition, CssDocConfiguration, type CssDocEntry, type CssDocSyntaxKind, CssDocTagDefinition, type CssDocTagDefinitionOptions, type CssFunction, type CssLayer, type CssModifier, type CssPart, type CssPropertyDeclared, type CssRecordKind, type CssReleaseStage, type CssSlot, type CssState, DEFAULT_MODIFIER_CONVENTION, type DocCondition, type DocCssProperty, type DocModifier, MODIFIER_PRESETS, type ModifierConvention, type ModifierConventionInput, type ModifierHit, ModifierMatcher, type ParseOptions, type ParsedDoc, RECORD_TAGS, type StructureNode, parseCssDocs, parseDocComment, parseStructure, recordNameOf, resolveModifierConvention, stripCommentFraming, toJson, toMermaid };
698
+ export { type CssAnimation, type CssCondition, CssDocConfiguration, type CssDocEntry, type CssDocSyntaxKind, CssDocTagDefinition, type CssDocTagDefinitionOptions, type CssFunction, type CssLayer, type CssModifier, type CssParse, type CssPart, type CssPropertyDeclared, type CssRecordKind, type CssReleaseStage, type CssSlot, type CssState, DEFAULT_MODIFIER_CONVENTION, DEFAULT_STATE_PSEUDO_CLASSES, type DocCondition, type DocCssProperty, type DocModifier, MODIFIER_PRESETS, type ModifierConvention, type ModifierConventionInput, type ModifierHit, ModifierMatcher, type ParseOptions, type ParsedDoc, RECORD_TAGS, type StructureNode, parseCssDocs, parseDocComment, parseStructure, recordNameOf, resolveModifierConvention, stripCommentFraming, toJson, toMermaid };