@rbxts/types 1.0.948 → 1.0.949

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.
Files changed (2) hide show
  1. package/include/selector.d.ts +195 -106
  2. package/package.json +1 -1
@@ -10,143 +10,232 @@
10
10
  * `Instance`; attribute filters and pseudo-classes never change the class. Note that
11
11
  * whitespace is not a combinator in this grammar (descendant matching is `>>`), so spaces
12
12
  * are ignored rather than treated as separators.
13
+ *
14
+ * Dynamic templates and selectors exceeding the bounded analysis budget fall back to
15
+ * `Instance`. Validation checks unsupported pseudo-classes and empty list entries; it
16
+ * is not a complete runtime grammar check.
13
17
  */
14
18
  declare namespace Selector {
15
- type Trim<T extends string> = T extends `${infer S} ` ? Trim<S> : T extends ` ${infer S}` ? Trim<S> : T;
16
-
17
- type SolveHead<Head extends string | undefined> = Head extends string
18
- ? Trim<Head> extends infer C extends keyof Instances
19
- ? Instances[C]
20
- : Instance
21
- : Instance;
22
-
23
- type AddSolvedHead<Result, Head extends string | undefined> = [Result] extends [never]
24
- ? SolveHead<Head>
25
- : Result | SolveHead<Head>;
26
-
27
- type HeadBreak = ":" | "." | "#" | "[" | " ";
28
-
29
- type ReadHead<
30
- Head extends string | undefined = undefined,
31
- Done extends boolean = false,
32
- C extends string = "",
33
- > = Done extends true
34
- ? [Head, Done]
35
- : Head extends undefined
36
- ? Trim<C> extends ""
37
- ? [undefined, false]
38
- : C extends HeadBreak
39
- ? ["", true]
40
- : [C, false]
41
- : C extends HeadBreak
42
- ? [Head, true]
43
- : [`${Head}${C}`, false];
44
-
45
- type SolveChars<
46
- S extends string,
47
- Head extends string | undefined = undefined,
48
- Done extends boolean = false,
49
- Result = never,
50
- Depth extends Array<any> = [],
51
- > = S extends `${infer C}${infer Rest}`
52
- ? Depth extends []
53
- ? C extends "("
54
- ? SolveChars<Rest, Head, Done, Result, [any]>
55
- : C extends ","
56
- ? SolveChars<Rest, undefined, false, AddSolvedHead<Result, Head>, Depth>
57
- : C extends ">"
58
- ? SolveChars<Rest, undefined, false, Result, Depth>
59
- : ReadHead<Head, Done, C> extends [
60
- infer NextHead extends string | undefined,
61
- infer NextDone extends boolean,
62
- ]
63
- ? SolveChars<Rest, NextHead, NextDone, Result, Depth>
64
- : never
65
- : C extends "("
66
- ? SolveChars<Rest, Head, Done, Result, [...Depth, any]>
67
- : C extends ")"
68
- ? SolveChars<Rest, Head, Done, Result, Depth extends [any, ...infer D] ? D : []>
69
- : SolveChars<Rest, Head, Done, Result, Depth>
70
- : AddSolvedHead<Result, Head>;
71
-
72
- // Nested pseudo-classes can contain selector lists, so this path tracks only top-level
73
- // separators while keeping the subject class from the current segment.
74
- type SlowSolve<T extends string> = SolveChars<T>;
19
+ // Bounds are deliberately below TypeScript's recursion limit. A broad string means
20
+ // analysis stopped; callers must preserve it as an Instance fallback, never a partial type.
21
+ type OtherWhitespace = "\t" | "\n" | "\r" | "\f" | "\v";
22
+ type Trim<T extends string, Steps extends Array<unknown> = []> = Steps["length"] extends 128
23
+ ? string
24
+ : T extends `${infer S} `
25
+ ? Trim<S, [...Steps, unknown]>
26
+ : T extends ` ${infer S}`
27
+ ? Trim<S, [...Steps, unknown]>
28
+ : T;
29
+
30
+ // Infinite template-literal key sets (including `${number}`) have no required keys.
31
+ // Unlike `string extends S`, this also detects selectors such as `Part.${string}`.
32
+ type IsDynamic<S extends string> = Record<never, never> extends Record<S, unknown> ? true : false;
75
33
 
76
34
  // Only the final combinator segment can determine the subject type.
77
- type LastSegment<S extends string> = S extends `${string}>${infer R}` ? LastSegment<R> : S;
35
+ type LastSegment<S extends string, Steps extends Array<unknown> = []> = Steps["length"] extends 128
36
+ ? string
37
+ : S extends `${string}>${infer R}`
38
+ ? LastSegment<R, [...Steps, unknown]>
39
+ : S;
78
40
 
79
41
  type CutAt<S extends string, D extends string> = S extends `${infer Prefix}${D}${string}` ? Prefix : S;
80
42
 
81
43
  type LeadingClass<S extends string> = CutAt<CutAt<CutAt<CutAt<Trim<S>, ":">, ".">, "#">, " ">;
82
44
 
83
- // Filters do not affect the subject class, but their values can contain selector separators.
84
- type StripFiltersAndParens<S extends string> = S extends `${infer A}[${string}]${infer B}`
85
- ? StripFiltersAndParens<`${A}${B}`>
86
- : S extends `${infer A}(${string})${infer B}`
87
- ? StripFiltersAndParens<`${A}${B}`>
88
- : S;
89
-
90
- type FastClause<S extends string> =
91
- LeadingClass<LastSegment<S>> extends infer C extends keyof Instances ? Instances[C] : Instance;
45
+ // Consume a quoted value without interpreting the other quote character as syntax.
46
+ // Values without backslashes use a single template match, regardless of their length.
47
+ type QuotedRest<S extends string, Quote extends "'" | '"'> = S extends `${infer A}${Quote}${infer B}`
48
+ ? A extends `${string}\\${string}`
49
+ ? EscapedQuotedRest<S, Quote>
50
+ : B
51
+ : string;
92
52
 
93
- type FastSolve<S extends string> = S extends `${infer A},${infer B}` ? FastClause<A> | FastSolve<B> : FastClause<S>;
53
+ type EscapedQuotedRest<
54
+ S extends string,
55
+ Quote extends string,
56
+ Steps extends Array<unknown> = [],
57
+ > = Steps["length"] extends 256
58
+ ? string
59
+ : S extends `\\${string}${infer Rest}`
60
+ ? EscapedQuotedRest<Rest, Quote, [...Steps, unknown]>
61
+ : S extends `${Quote}${infer Rest}`
62
+ ? Rest
63
+ : S extends `${string}${infer Rest}`
64
+ ? EscapedQuotedRest<Rest, Quote, [...Steps, unknown]>
65
+ : string;
66
+
67
+ type StripQuote<
68
+ S extends string,
69
+ Quote extends "'" | '"',
70
+ Out extends string,
71
+ Steps extends Array<unknown>,
72
+ > = S extends `${infer A}${Quote}${infer B}`
73
+ ? QuotedRest<B, Quote> extends infer Rest extends string
74
+ ? string extends Rest
75
+ ? string
76
+ : StripQuotes<Rest, `${Out}${A}`, [...Steps, unknown]>
77
+ : never
78
+ : `${Out}${S}`;
94
79
 
95
- type SolveUnquoted<S extends string> = S extends `${string}(${string}(${string})${string})${string}`
96
- ? SlowSolve<S>
97
- : FastSolve<StripFiltersAndParens<S>>;
80
+ type StripQuotes<
81
+ S extends string,
82
+ Out extends string = "",
83
+ Steps extends Array<unknown> = [],
84
+ > = Steps["length"] extends 128
85
+ ? string
86
+ : S extends `${infer A}'${string}`
87
+ ? A extends `${string}"${string}`
88
+ ? StripQuote<S, '"', Out, Steps>
89
+ : StripQuote<S, "'", Out, Steps>
90
+ : S extends `${string}"${string}`
91
+ ? StripQuote<S, '"', Out, Steps>
92
+ : `${Out}${S}`;
93
+
94
+ // Quoted delimiters must be removed before filters or pseudo-class groups are read.
95
+ // Sharing this normalization between validation and inference also lets the checker cache it.
96
+ // A dot is an opaque filter marker: it preserves a nonempty selector inside :not([$x]).
97
+ type StripFilters<S extends string, Steps extends Array<unknown> = []> = Steps["length"] extends 128
98
+ ? string
99
+ : S extends `${infer A}[${string}]${infer B}`
100
+ ? StripFilters<`${A}.${B}`, [...Steps, unknown]>
101
+ : S;
102
+ type ReplaceWhitespace<
103
+ S extends string,
104
+ W extends string,
105
+ Steps extends Array<unknown> = [],
106
+ > = Steps["length"] extends 128
107
+ ? string
108
+ : S extends `${infer A}${W}${infer B}`
109
+ ? ReplaceWhitespace<`${A} ${B}`, W, [...Steps, unknown]>
110
+ : S;
111
+ type NormalizeWhitespace<S extends string> = S extends `${string}${OtherWhitespace}${string}`
112
+ ? ReplaceWhitespace<
113
+ ReplaceWhitespace<ReplaceWhitespace<ReplaceWhitespace<ReplaceWhitespace<S, "\t">, "\n">, "\r">, "\f">,
114
+ "\v"
115
+ >
116
+ : S;
117
+ type Normalize<S extends string> = NormalizeWhitespace<StripFilters<StripQuotes<S>>>;
118
+
119
+ // Jump between parentheses rather than visiting every character. Quoted values and
120
+ // filters are already opaque, so only these delimiters affect nesting.
121
+ type StripClose<
122
+ S extends string,
123
+ Out extends string,
124
+ Depth extends Array<unknown>,
125
+ Steps extends Array<unknown>,
126
+ > = S extends `${string})${infer Rest}`
127
+ ? Depth extends [unknown, ...infer D]
128
+ ? StripParens<Rest, Out, D, [...Steps, unknown]>
129
+ : string
130
+ : string;
131
+
132
+ type StripParens<
133
+ S extends string,
134
+ Out extends string = "",
135
+ Depth extends Array<unknown> = [],
136
+ Steps extends Array<unknown> = [],
137
+ > = Steps["length"] extends 128
138
+ ? string
139
+ : S extends `${infer A}(${infer B}`
140
+ ? A extends `${string})${string}`
141
+ ? StripClose<S, Out, Depth, Steps>
142
+ : StripParens<B, Depth extends [] ? `${Out}${A}` : Out, [...Depth, unknown], [...Steps, unknown]>
143
+ : S extends `${string})${string}`
144
+ ? StripClose<S, Out, Depth, Steps>
145
+ : Depth extends []
146
+ ? `${Out}${S}`
147
+ : string;
148
+
149
+ type FastClause<S extends string> = S extends keyof Instances
150
+ ? Instances[S]
151
+ : LeadingClass<LastSegment<S>> extends infer C extends keyof Instances
152
+ ? Instances[C]
153
+ : Instance;
98
154
 
99
- type StripQuotes<S extends string> = S extends `${infer A}'${string}'${infer B}` ? StripQuotes<`${A}${B}`> : S;
155
+ // Accumulate the union so long lists remain tail-recursive.
156
+ type FastSolve<S extends string, Result = never, Steps extends Array<unknown> = []> = Steps["length"] extends 128
157
+ ? Instance
158
+ : S extends `${infer A},${infer B}`
159
+ ? FastSolve<B, Result | FastClause<A>, [...Steps, unknown]>
160
+ : Result | FastClause<S>;
100
161
 
101
- // Attribute values are not selector syntax, even when they contain ":" or ",".
102
- type StripValidationGroups<S extends string> = S extends `${infer A}'${string}'${infer B}`
103
- ? StripValidationGroups<`${A}${B}`>
104
- : S extends `${infer A}[${string}]${infer B}`
105
- ? StripValidationGroups<`${A}${B}`>
162
+ type StripFlatParens<S extends string, Steps extends Array<unknown> = []> = Steps["length"] extends 128
163
+ ? string
164
+ : S extends `${infer A}(${string})${infer B}`
165
+ ? StripFlatParens<`${A}${B}`, [...Steps, unknown]>
106
166
  : S;
107
167
 
168
+ type SolveNormalized<S extends string> = string extends S
169
+ ? Instance
170
+ : S extends `${string}(${string}`
171
+ ? S extends `${string}(${string}(${string})${string})${string}`
172
+ ? FastSolve<StripParens<S>>
173
+ : FastSolve<StripFlatParens<S>>
174
+ : S extends `${string},${string}`
175
+ ? FastSolve<S>
176
+ : FastClause<S>;
177
+
108
178
  type SupportedPseudo = "not" | "has";
109
179
 
110
- type CheckPseudos<S extends string> = S extends `${string}:${infer R}`
111
- ? R extends `${infer Name}(${infer Rest}`
112
- ? Name extends SupportedPseudo
113
- ? CheckPseudos<Rest>
114
- : Name
115
- : R // ':' not followed by 'name(' -> pseudo-classes require arguments
116
- : never;
180
+ type CheckPseudos<S extends string, Steps extends Array<unknown> = []> = Steps["length"] extends 128
181
+ ? never
182
+ : S extends `${string}:${infer R}`
183
+ ? R extends `${infer Name}(${infer Rest}`
184
+ ? Name extends SupportedPseudo
185
+ ? CheckPseudos<Rest, [...Steps, unknown]>
186
+ : Name
187
+ : R // ':' not followed by 'name(' -> pseudo-classes require arguments
188
+ : never;
189
+
190
+ // Parentheses delimit nested lists just as commas delimit their entries.
191
+ type HasEmptyGroup<S extends string, Steps extends Array<unknown> = []> = Steps["length"] extends 128
192
+ ? false
193
+ : S extends `${string}(${infer Rest}`
194
+ ? Trim<Rest> extends `)${string}` | `,${string}`
195
+ ? true
196
+ : HasEmptyGroup<Rest, [...Steps, unknown]>
197
+ : false;
117
198
 
118
199
  // The whole-empty selector "" is intentionally allowed.
119
200
  type HasEmptyListItem<S extends string> = S extends `${string},${string}` ? CheckListItems<S> : false;
120
- type CheckListItems<S extends string> = S extends `${infer A},${infer B}`
121
- ? Trim<A> extends ""
122
- ? true
123
- : CheckListItems<B>
124
- : Trim<S> extends ""
125
- ? true
126
- : false;
201
+ type CheckListItems<S extends string, Steps extends Array<unknown> = []> = Steps["length"] extends 128
202
+ ? false
203
+ : S extends `${infer A},${infer B}`
204
+ ? Trim<A> extends "" | `${string}(` | `)${string}`
205
+ ? true
206
+ : CheckListItems<B, [...Steps, unknown]>
207
+ : Trim<S> extends "" | `)${string}`
208
+ ? true
209
+ : false;
127
210
 
128
211
  type ValidateUnquoted<S extends string, Q extends string> = Q extends `${string}:${string}`
129
212
  ? CheckPseudos<Q> extends infer Bad
130
213
  ? [Bad] extends [never]
131
- ? HasEmptyListItem<Q> extends true
214
+ ? true extends HasEmptyListItem<Q> | HasEmptyGroup<Q>
132
215
  ? `Invalid selector: empty selector in list (check for a stray or trailing comma)`
133
216
  : S
134
217
  : `Invalid selector: ':${Bad & string}' is not a supported pseudo-class (only ':not()' and ':has()' are allowed)`
135
218
  : never
136
219
  : Q extends `${string},${string}`
137
- ? HasEmptyListItem<Q> extends true
220
+ ? true extends HasEmptyListItem<Q> | HasEmptyGroup<Q>
138
221
  ? `Invalid selector: empty selector in list (check for a stray or trailing comma)`
139
222
  : S
140
223
  : S;
141
224
 
142
- export type ValidateSelector<S extends string> = string extends S
143
- ? S
144
- : StripValidationGroups<S> extends infer Q extends string
145
- ? ValidateUnquoted<S, Q>
146
- : never;
147
-
148
- // Quoted values are stripped before solving so most selectors can stay on the cheaper path.
149
- export type Solve<S extends string> = S extends `${string}'${string}`
150
- ? SolveUnquoted<StripQuotes<S>>
151
- : SolveUnquoted<S>;
225
+ // Distribute over the original selector so each normalized branch retains its own input.
226
+ export type ValidateSelector<S extends string> = S extends `${string}${":" | ","}${string}`
227
+ ? IsDynamic<S> extends true
228
+ ? S
229
+ : Normalize<S> extends infer Q extends string
230
+ ? string extends Q
231
+ ? S
232
+ : ValidateUnquoted<S, Q>
233
+ : never
234
+ : S;
235
+
236
+ export type Solve<S extends string> = S extends keyof Instances
237
+ ? Instances[S]
238
+ : IsDynamic<S> extends true
239
+ ? Instance
240
+ : SolveNormalized<Normalize<S>>;
152
241
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rbxts/types",
3
- "version": "1.0.948",
3
+ "version": "1.0.949",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },