UNPKG

11.5 kBTypeScriptView Raw
1/**
2 * CSS Selector AST root.
3 * Contains list of CSS rules (separated by a comma in the input CSS selector string).
4 * Generated by {@link AstFactory.selector ast.selector}.
5 */
6export interface AstSelector {
7 type: 'Selector';
8 /**
9 * List of CSS rules. Every rule contains conditions. Selector is considered matched once at least one rule matches.
10 */
11 rules: AstRule[];
12}
13/**
14 * A single CSS rule that contains match conditions.
15 * Can nest another rule with or without a combinator (i.e. `"div > span"`).
16 * Generated by {@link AstFactory.rule ast.rule}.
17 */
18export interface AstRule {
19 type: 'Rule';
20 /** Items of a CSS rule. Can be tag, ids, class names, pseudo-classes and pseudo-elements. */
21 items: (AstTagName | AstWildcardTag | AstId | AstClassName | AstAttribute | AstPseudoClass | AstPseudoElement)[];
22 /** Rule combinator which was used to nest this rule (i.e. `">"` in case of `"div > span"` if the current rule is `"span"`). */
23 combinator?: string;
24 /** Nested rule if specified (i.e. `"div > span"`). */
25 nestedRule?: AstRule;
26}
27/**
28 * Named tag, i.e. `"div"`. Part of CSS Qualified Names.
29 * Generated by {@link AstFactory.tagName ast.tagName}.
30 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/Type_selectors
31 * @see https://drafts.csswg.org/css-namespaces-3/#css-qnames
32 */
33export interface AstTagName {
34 type: 'TagName';
35 /** Tag name, i.e. `"div"`. */
36 name: string;
37 /** Namespace according to https://www.w3.org/TR/css3-namespace/. */
38 namespace?: AstNamespaceName | AstWildcardNamespace | AstNoNamespace;
39}
40/**
41 * ID condition. Matches by id attribute value.
42 * Generated by {@link AstFactory.id ast.id}.
43 * https://developer.mozilla.org/en-US/docs/Web/CSS/ID_selectors
44 * @example "#root"
45 */
46export interface AstId {
47 type: 'Id';
48 /** ID name. I.e. `#root` -> `"root"`. */
49 name: string;
50}
51/**
52 * Class name condition. Matches by class attribute value.
53 * Generated by {@link AstFactory.className ast.className}.
54 * https://developer.mozilla.org/en-US/docs/Web/CSS/ID_selectors
55 * @example ".user"
56 */
57export interface AstClassName {
58 type: 'ClassName';
59 /** ID name. I.e. `.user` -> `"user"`. */
60 name: string;
61}
62/**
63 * Wildcard tag (universal selector): `*`.
64 * Generated by {@link AstFactory.wildcardTag ast.wildcardTag}.
65 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/Universal_selectors
66 * @see https://drafts.csswg.org/css-namespaces-3/#css-qnames
67 */
68export interface AstWildcardTag {
69 type: 'WildcardTag';
70 /** Namespace according to https://www.w3.org/TR/css3-namespace/. */
71 namespace?: AstNamespaceName | AstWildcardNamespace | AstNoNamespace;
72}
73/**
74 * Named namespace declaration (i.e. `ns|div`).
75 * Generated by {@link AstFactory.namespaceName ast.namespaceName}.
76 * @see https://drafts.csswg.org/css-namespaces-3/#css-qnames
77 */
78export interface AstNamespaceName {
79 type: 'NamespaceName';
80 /** Namespace name (i.e. `"ns"` in case of `"ns|div"`). "*/
81 name: string;
82}
83/**
84 * Wildcard namespace (universal selector): `*`.
85 * Generated by {@link AstFactory.wildcardNamespace ast.wildcardNamespace}.
86 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/Universal_selectors
87 * @see https://drafts.csswg.org/css-namespaces-3/#css-qnames
88 */
89export interface AstWildcardNamespace {
90 type: 'WildcardNamespace';
91}
92/**
93 * Explicit no-namespace declaration (i.e. `|div`).
94 * Generated by {@link AstFactory.noNamespace ast.noNamespace}.
95 * @see https://drafts.csswg.org/css-namespaces-3/#css-qnames
96 */
97export interface AstNoNamespace {
98 type: 'NoNamespace';
99}
100/**
101 * Attribute selector.
102 * Generated by {@link AstFactory.attribute ast.attribute}.
103 * @example "[role='button' i]"
104 */
105export interface AstAttribute {
106 type: 'Attribute';
107 /** Attribute name (i.e. `"href"` in case if `"[href]"`). */
108 name: string;
109 /** Namespace according to https://drafts.csswg.org/selectors/#attrnmsp. */
110 namespace?: AstNamespaceName | AstWildcardNamespace | AstNoNamespace;
111 /** Comparison operator (i.e. `"|="` in case if `"[role|=button]"`). */
112 operator?: string;
113 /** Comparison value (i.e. `"button"` in case if `"[role=button]"`). */
114 value?: AstString | AstSubstitution;
115 /** Comparison case sensitivity modifier (i.e. `"i"` in case if `"[role='button' i]"`). */
116 caseSensitivityModifier?: string;
117}
118/**
119 * Pseudo-class selector.
120 * Generated by {@link AstFactory.pseudoClass ast.pseudoClass}.
121 * @see https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Selectors/Pseudo-classes_and_pseudo-elements
122 * @example ":lang(en)"
123 */
124export interface AstPseudoClass {
125 type: 'PseudoClass';
126 /** Pseudo-class name (i.e. `"hover"` in case of `":hover"`). */
127 name: string;
128 /** Pseudo-class value (i.e. `"en"` in case of `":lang(en)"`). */
129 argument?: AstSubstitution | AstSelector | AstString | AstFormula | AstFormulaOfSelector;
130}
131/**
132 * Pseudo-class selector.
133 * Generated by {@link AstFactory.pseudoElement ast.pseudoElement}.
134 * @see https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Selectors/Pseudo-classes_and_pseudo-elements
135 * @example "::before"
136 */
137export interface AstPseudoElement {
138 type: 'PseudoElement';
139 /** Pseudo-element name (i.e. `"before"` in case of `"::before"`). */
140 name: string;
141 /** Pseudo-element value (i.e. `"foo"` in case of `"::part(foo)"`). */
142 argument?: AstSubstitution | AstString | AstSelector;
143}
144/**
145 * String value. Can be used as attribute value of pseudo-class string value.
146 * For instance `:lang(en)` -> `{type: 'AstPseudoClass'..., argument: {type: 'String', value: 'en'}}`.
147 * Generated by {@link AstFactory.string ast.string}.
148 */
149export interface AstString {
150 type: 'String';
151 /** The actual string value. */
152 value: string;
153}
154/**
155 * Pseudo-class formula value. `a` is multiplier of `n` and `b` us added on top. Formula: `an + b`.
156 * For instance `:nth-child(2n + 1)` -> `{type: 'AstPseudoClass'..., argument: {type: 'Formula', a: 2, b: 1}}`.
157 * Generated by {@link AstFactory.formula ast.formula}.
158 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-child#functional_notation
159 */
160export interface AstFormula {
161 type: 'Formula';
162 /** Multiplier of `n`. */
163 a: number;
164 /** Constant added to `a*n`. */
165 b: number;
166}
167/**
168 * Pseudo-class formula of selector value. `a` is multiplier of `n` and `b` us added on top. Formula: `an + b`.
169 * Formula is followed by `of` keyword and then goes a CSS selector.
170 * For instance `:nth-child(2n + 1 of div)` ->
171 * `{type: 'AstPseudoClass'..., argument: {type: 'FormulaOfSelector', a: 2, b: 1, selector: {type: 'Selector', rules: [{type: 'Rule', items: [{type: 'TagName', name: 'div'}]}]}}}`.
172 * Generated by {@link AstFactory.formulaOfSelector ast.formulaOfSelector}.
173 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-child#functional_notation
174 */
175export interface AstFormulaOfSelector {
176 type: 'FormulaOfSelector';
177 /** Multiplier of `n`. */
178 a: number;
179 /** Constant added to `a*n`. */
180 b: number;
181 /** Selector that goes after formula (i.e. `"div -> span"` in case of `":nth-child(2n + 1 of div > span)"` */
182 selector: AstRule;
183}
184/**
185 * Substitution is not part of CSS spec, but rather a useful extension on top of CSS if you need to pass variables.
186 * Generated by {@link AstFactory.substitution ast.substitution}.
187 */
188export interface AstSubstitution {
189 type: 'Substitution';
190 /** Substitution name (i.e. "var" in case of `"[role=$var]"` or `":lang($var)"`). */
191 name: string;
192}
193/** One of pseudo-class argument types. */
194export type AstPseudoClassArgument = AstSubstitution | AstSelector | AstString | AstFormula | AstFormulaOfSelector;
195/** One of pseudo-element argument types. */
196export type AstPseudoElementArgument = AstSubstitution | AstString | AstSelector;
197/** One of CSS AST entity types. */
198export type AstEntity = AstSelector | AstRule | AstTagName | AstWildcardTag | AstId | AstClassName | AstNamespaceName | AstWildcardNamespace | AstNoNamespace | AstSubstitution | AstString | AstFormula | AstFormulaOfSelector | AstPseudoClass | AstAttribute | AstPseudoElement;
199type UnionToIntersection<T> = (T extends any ? (x: T) => any : never) extends (x: infer R) => any ? R : never;
200type ToAstFactory<T> = UnionToIntersection<T extends {
201 type: infer Type;
202} ? Type extends string ? {
203 [K in Uncapitalize<Type>]: {} extends Omit<T, 'type'> ? (props?: {
204 [PK in keyof Omit<T, 'type'>]: Omit<T, 'type'>[PK];
205 }) => T : (props: {
206 [PK in keyof Omit<T, 'type'>]: Omit<T, 'type'>[PK];
207 }) => T;
208} & {
209 [K in `is${Type}`]: (entity: unknown) => entity is T;
210} : never : never>;
211/** @internal */
212type AstFactoryBase = {
213 [K in keyof ToAstFactory<AstEntity>]: ToAstFactory<AstEntity>[K];
214};
215/**
216 * AST structure generators and matchers.
217 * For instance, `ast.selector({rules: [...]})` creates AstSelector and `ast.isSelector(...)` checks if
218 * AstSelector was specified.
219 *
220 * @example
221 *
222 * // Represents CSS selector: ns|div#user-34.user.user-active[role="button"]:lang(en)::before > *
223 * const selector = ast.selector({
224 * rules: [
225 * ast.rule({
226 * items: [
227 * ast.tagName({name: 'div', namespace: ast.namespaceName({name: 'ns'})}),
228 * ast.id({name: 'user-34'}),
229 * ast.className({name: 'user'}),
230 * ast.className({name: 'user-active'}),
231 * ast.attribute({
232 * name: 'role',
233 * operator: '=',
234 * value: ast.string({value: 'button'})
235 * }),
236 * ast.pseudoClass({
237 * name: 'lang',
238 * argument: ast.string({value: 'en'})
239 * }),
240 * ast.pseudoElement({name: 'before'})
241 * ],
242 * nestedRule: ast.rule({combinator: '>', items: [ast.wildcardTag()]})
243 * })
244 * ]
245 * });
246 * console.log(ast.isSelector(selector)); // prints true
247 * console.log(ast.isRule(selector)); // prints false
248 */
249export interface AstFactory extends AstFactoryBase {
250}
251/**
252 * AST structure generators and matchers.
253 * For instance, `ast.selector({rules: [...]})` creates AstSelector and `ast.isSelector(...)` checks if
254 * AstSelector was specified.
255 *
256 * @example
257 *
258 * // Represents CSS selector: ns|div#user-34.user.user-active[role="button"]:lang(en)::before > *
259 * const selector = ast.selector({
260 * rules: [
261 * ast.rule({
262 * items: [
263 * ast.tagName({name: 'div', namespace: ast.namespaceName({name: 'ns'})}),
264 * ast.id({name: 'user-34'}),
265 * ast.className({name: 'user'}),
266 * ast.className({name: 'user-active'}),
267 * ast.attribute({
268 * name: 'role',
269 * operator: '=',
270 * value: ast.string({value: 'button'})
271 * }),
272 * ast.pseudoClass({
273 * name: 'lang',
274 * argument: ast.string({value: 'en'})
275 * }),
276 * ast.pseudoElement({name: 'before'})
277 * ],
278 * nestedRule: ast.rule({combinator: '>', items: [ast.wildcardTag()]})
279 * })
280 * ]
281 * });
282 * console.log(ast.isSelector(selector)); // prints true
283 * console.log(ast.isRule(selector)); // prints false
284 */
285export declare const ast: AstFactory;
286export {};