@thejaredwilcurt/csslop 0.0.38 → 0.0.40

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.
@@ -0,0 +1,291 @@
1
+ /**
2
+ * @file Describes every at-rule the parser knows by name, so the parser can read one the same way no matter which at-rule it is.
3
+ */
4
+
5
+ /**
6
+ * The ways an at-rule's body can be written, which is what decides how the
7
+ * parser reads whatever follows the at-rule's prelude.
8
+ *
9
+ * `nestedRules` is a block that holds rules, nested at-rules, or the
10
+ * declarations of a nesting parent. `declarations` is a block that only
11
+ * describes something, such as a font or a custom property, so it holds
12
+ * declarations alone. `statement` has no block at all and ends at a
13
+ * semicolon. `keyframes` and `page` each hold a kind of child that exists
14
+ * nowhere else in CSS, so the parser reads their bodies itself.
15
+ */
16
+ const AT_RULE_BODY = {
17
+ declarations: 'declarations',
18
+ keyframes: 'keyframes',
19
+ nestedRules: 'nested-rules',
20
+ page: 'page',
21
+ statement: 'statement'
22
+ };
23
+
24
+ /**
25
+ * Matches the condition of an at-rule that is written with spaces in front of
26
+ * it and ends where the at-rule's block begins.
27
+ *
28
+ * @type {RegExp}
29
+ */
30
+ // Spaces, then everything up to the opening brace of the block
31
+ const CONDITION_PATTERN = / *([^{]+)/y;
32
+
33
+ /**
34
+ * Matches the condition of an at-rule whose condition may be left out
35
+ * entirely, such as a `@scope` that only limits its lower boundary.
36
+ *
37
+ * @type {RegExp}
38
+ */
39
+ // Leading whitespace, then everything (possibly nothing) up to the opening brace
40
+ const OPTIONAL_CONDITION_PATTERN = /\s*([^{]*)/y;
41
+
42
+ /**
43
+ * Matches the condition of an at-rule that has to be separated from its name
44
+ * by whitespace, because the condition begins with an identifier rather than
45
+ * with punctuation.
46
+ *
47
+ * @type {RegExp}
48
+ */
49
+ // Required whitespace, then everything up to the opening brace of the block
50
+ const REQUIRED_CONDITION_PATTERN = /\s+([^{]+)/y;
51
+
52
+ /**
53
+ * Matches the names a `@layer` block or statement declares. An at-sign ends
54
+ * the list because a layer name cannot contain one, so an unterminated
55
+ * `@layer` gives way to the at-rule that follows it.
56
+ *
57
+ * @type {RegExp}
58
+ */
59
+ // Spaces, then the layer names, which stop at a block, a semicolon, or another at-rule
60
+ const LAYER_NAMES_PATTERN = / *([^{;@]+)/y;
61
+
62
+ /**
63
+ * Matches the dashed identifier that names a `@property` or a
64
+ * `@position-try`.
65
+ *
66
+ * @type {RegExp}
67
+ */
68
+ // Whitespace, the dashed identifier, then the whitespace before the block
69
+ const DASHED_NAME_PATTERN = /\s+(--[-\w]+)\s*/y;
70
+
71
+ /**
72
+ * Matches the identifier that names a `@counter-style` or a `@keyframes`.
73
+ *
74
+ * @type {RegExp}
75
+ */
76
+ // Whitespace, the identifier, then the whitespace before the block
77
+ const IDENTIFIER_NAME_PATTERN = /\s*([-\w]+)\s*/y;
78
+
79
+ /**
80
+ * Matches nothing but the whitespace between an at-rule that has no prelude
81
+ * and the block that follows it.
82
+ *
83
+ * @type {RegExp}
84
+ */
85
+ // The whitespace, if any, in front of the block
86
+ const NO_PRELUDE_PATTERN = /\s*/y;
87
+
88
+ /**
89
+ * Every at-rule that is named exactly, mapped to the node it produces and the
90
+ * way its prelude and body are written. At-rules whose name carries a vendor
91
+ * prefix, and the margin boxes of `@page`, are matched separately.
92
+ *
93
+ * @type {object}
94
+ */
95
+ const AT_RULE_DEFINITIONS = {
96
+ charset: {
97
+ type: 'charset',
98
+ body: AT_RULE_BODY.statement,
99
+ preludeFields: ['charset']
100
+ },
101
+ container: {
102
+ type: 'container',
103
+ body: AT_RULE_BODY.nestedRules,
104
+ preludePattern: CONDITION_PATTERN,
105
+ preludeFields: ['container']
106
+ },
107
+ 'counter-style': {
108
+ type: 'counter-style',
109
+ body: AT_RULE_BODY.declarations,
110
+ preludePattern: IDENTIFIER_NAME_PATTERN,
111
+ preludeFields: ['name']
112
+ },
113
+ 'custom-media': {
114
+ type: 'custom-media',
115
+ body: AT_RULE_BODY.statement,
116
+ preludeFields: ['name', 'media']
117
+ },
118
+ 'font-face': {
119
+ type: 'font-face',
120
+ body: AT_RULE_BODY.declarations,
121
+ preludePattern: NO_PRELUDE_PATTERN,
122
+ preludeFields: []
123
+ },
124
+ 'font-feature-values': {
125
+ type: 'font-feature-values',
126
+ body: AT_RULE_BODY.nestedRules,
127
+ preludePattern: REQUIRED_CONDITION_PATTERN,
128
+ preludeFields: ['fontFamily']
129
+ },
130
+ host: {
131
+ type: 'host',
132
+ body: AT_RULE_BODY.nestedRules,
133
+ preludePattern: NO_PRELUDE_PATTERN,
134
+ preludeFields: []
135
+ },
136
+ import: {
137
+ type: 'import',
138
+ body: AT_RULE_BODY.statement,
139
+ preludeFields: ['import']
140
+ },
141
+ layer: {
142
+ type: 'layer',
143
+ body: AT_RULE_BODY.nestedRules,
144
+ preludePattern: LAYER_NAMES_PATTERN,
145
+ preludeFields: ['layer'],
146
+ statementFallback: true
147
+ },
148
+ media: {
149
+ type: 'media',
150
+ body: AT_RULE_BODY.nestedRules,
151
+ preludePattern: CONDITION_PATTERN,
152
+ preludeFields: ['media']
153
+ },
154
+ namespace: {
155
+ type: 'namespace',
156
+ body: AT_RULE_BODY.statement,
157
+ preludeFields: ['namespace']
158
+ },
159
+ page: {
160
+ type: 'page',
161
+ body: AT_RULE_BODY.page,
162
+ preludePattern: NO_PRELUDE_PATTERN,
163
+ preludeFields: []
164
+ },
165
+ 'position-try': {
166
+ type: 'position-try',
167
+ body: AT_RULE_BODY.declarations,
168
+ preludePattern: DASHED_NAME_PATTERN,
169
+ preludeFields: ['name']
170
+ },
171
+ property: {
172
+ type: 'property',
173
+ body: AT_RULE_BODY.declarations,
174
+ preludePattern: DASHED_NAME_PATTERN,
175
+ preludeFields: ['name']
176
+ },
177
+ scope: {
178
+ type: 'scope',
179
+ body: AT_RULE_BODY.nestedRules,
180
+ preludePattern: OPTIONAL_CONDITION_PATTERN,
181
+ preludeFields: ['scope']
182
+ },
183
+ 'starting-style': {
184
+ type: 'starting-style',
185
+ body: AT_RULE_BODY.nestedRules,
186
+ preludePattern: NO_PRELUDE_PATTERN,
187
+ preludeFields: []
188
+ },
189
+ supports: {
190
+ type: 'supports',
191
+ body: AT_RULE_BODY.nestedRules,
192
+ preludePattern: CONDITION_PATTERN,
193
+ preludeFields: ['supports']
194
+ },
195
+ 'view-transition': {
196
+ type: 'view-transition',
197
+ body: AT_RULE_BODY.declarations,
198
+ preludePattern: NO_PRELUDE_PATTERN,
199
+ preludeFields: []
200
+ }
201
+ };
202
+
203
+ /**
204
+ * The at-rules whose name may carry a vendor prefix, such as
205
+ * `@-webkit-keyframes`. The prefix is reported separately from the node's
206
+ * other fields so that it can be written back out.
207
+ *
208
+ * @type {object}
209
+ */
210
+ const VENDOR_PREFIXED_AT_RULES = {
211
+ document: {
212
+ type: 'document',
213
+ body: AT_RULE_BODY.nestedRules,
214
+ preludePattern: CONDITION_PATTERN,
215
+ preludeFields: ['document']
216
+ },
217
+ keyframes: {
218
+ type: 'keyframes',
219
+ body: AT_RULE_BODY.keyframes,
220
+ preludePattern: IDENTIFIER_NAME_PATTERN,
221
+ preludeFields: ['name']
222
+ }
223
+ };
224
+
225
+ /**
226
+ * The margin boxes a `@page` rule may contain, each of which is written as an
227
+ * at-rule holding declarations.
228
+ *
229
+ * @type {Set<string>}
230
+ */
231
+ const PAGE_MARGIN_BOX_NAMES = new Set([
232
+ 'top-left-corner',
233
+ 'top-left',
234
+ 'top-center',
235
+ 'top-right',
236
+ 'top-right-corner',
237
+ 'bottom-left-corner',
238
+ 'bottom-left',
239
+ 'bottom-center',
240
+ 'bottom-right',
241
+ 'bottom-right-corner',
242
+ 'left-top',
243
+ 'left-middle',
244
+ 'left-bottom',
245
+ 'right-top',
246
+ 'right-middle',
247
+ 'right-bottom'
248
+ ]);
249
+
250
+ /**
251
+ * The definition shared by every margin box of a `@page` rule, whose own name
252
+ * is the only thing that tells them apart.
253
+ *
254
+ * @type {object}
255
+ */
256
+ const PAGE_MARGIN_BOX_DEFINITION = {
257
+ type: 'page-margin-box',
258
+ body: AT_RULE_BODY.declarations,
259
+ preludePattern: NO_PRELUDE_PATTERN,
260
+ preludeFields: []
261
+ };
262
+
263
+ /**
264
+ * Finds how an at-rule of a given name is written, including the vendor
265
+ * prefix when the name carries one. An unknown name has no definition, and is
266
+ * read as a generic at-rule instead.
267
+ *
268
+ * @param {string} name The at-rule's name, without its at-sign.
269
+ * @return {object|null} The definition of the at-rule, or null when the name is unknown.
270
+ */
271
+ function findAtRuleDefinition (name) {
272
+ const definition = AT_RULE_DEFINITIONS[name];
273
+ if (definition) {
274
+ return definition;
275
+ }
276
+ if (PAGE_MARGIN_BOX_NAMES.has(name)) {
277
+ return { ...PAGE_MARGIN_BOX_DEFINITION, name };
278
+ }
279
+ for (const baseName in VENDOR_PREFIXED_AT_RULES) {
280
+ if (name.endsWith(baseName)) {
281
+ const vendor = name.slice(0, name.length - baseName.length);
282
+ return { ...VENDOR_PREFIXED_AT_RULES[baseName], vendor };
283
+ }
284
+ }
285
+ return null;
286
+ }
287
+
288
+ export {
289
+ AT_RULE_BODY,
290
+ findAtRuleDefinition
291
+ };