rainbowindex 0.4.0 → 0.5.0
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/CHANGELOG.md +163 -0
- package/README.md +5 -4
- package/dist/browser.mjs +1 -1
- package/dist/{chunk-TLR6RP5L.mjs → chunk-CMB6BHVE.mjs} +27 -10
- package/dist/{chunk-4SVFFDS2.mjs → chunk-DT5HYIM3.mjs} +240 -65
- package/dist/{chunk-5Y7EXXLS.mjs → chunk-KRZL4IDK.mjs} +18 -5
- package/dist/{chunk-YLB6FGIG.mjs → chunk-NXJZX6KI.mjs} +2 -2
- package/dist/{chunk-6DAHUFNU.mjs → chunk-ZI5ZYNSU.mjs} +917 -630
- package/dist/cli.mjs +93 -38
- package/dist/editor.d.ts +2 -2
- package/dist/editor.mjs +4 -4
- package/dist/{index-BB2HoeMj.d.ts → index-Dp6i5TSv.d.ts} +15 -11
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +4 -4
- package/dist/vite.mjs +36 -60
- package/package.json +2 -1
|
@@ -42,7 +42,91 @@ import {
|
|
|
42
42
|
registerCustomTextSizes,
|
|
43
43
|
registerCustomUtility,
|
|
44
44
|
snapshotCompilationContext
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-KRZL4IDK.mjs";
|
|
46
|
+
|
|
47
|
+
// src/shared.ts
|
|
48
|
+
function isRIDebug() {
|
|
49
|
+
return typeof process !== "undefined" && !!process.env.RI_DEBUG;
|
|
50
|
+
}
|
|
51
|
+
function withTimeout(promise, ms, message) {
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
const timer = setTimeout(() => reject(new Error(message)), ms);
|
|
54
|
+
if (typeof timer === "object" && "unref" in timer) timer.unref();
|
|
55
|
+
promise.then(
|
|
56
|
+
(value) => {
|
|
57
|
+
clearTimeout(timer);
|
|
58
|
+
resolve(value);
|
|
59
|
+
},
|
|
60
|
+
(err) => {
|
|
61
|
+
clearTimeout(timer);
|
|
62
|
+
reject(err);
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function stripCSSComments(input) {
|
|
68
|
+
if (!input.includes("/")) return input;
|
|
69
|
+
const len = input.length;
|
|
70
|
+
let result = "";
|
|
71
|
+
let copyStart = 0;
|
|
72
|
+
let i = 0;
|
|
73
|
+
let quote = 0;
|
|
74
|
+
while (i < len) {
|
|
75
|
+
const c = input.charCodeAt(i);
|
|
76
|
+
if (quote !== 0) {
|
|
77
|
+
if (c === 92) {
|
|
78
|
+
i += 2;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (c === quote) quote = 0;
|
|
82
|
+
i++;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (c === 34 || c === 39) {
|
|
86
|
+
quote = c;
|
|
87
|
+
i++;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (c === 47) {
|
|
91
|
+
const next = input.charCodeAt(i + 1);
|
|
92
|
+
if (next === 42) {
|
|
93
|
+
result += input.slice(copyStart, i);
|
|
94
|
+
result += " ";
|
|
95
|
+
const end = input.indexOf("*/", i + 2);
|
|
96
|
+
copyStart = end === -1 ? len : end + 2;
|
|
97
|
+
if (end === -1) break;
|
|
98
|
+
i = copyStart;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (next === 47) {
|
|
102
|
+
result += input.slice(copyStart, i);
|
|
103
|
+
const end = input.indexOf("\n", i + 2);
|
|
104
|
+
copyStart = end === -1 ? len : end + 1;
|
|
105
|
+
if (end === -1) break;
|
|
106
|
+
result += "\n";
|
|
107
|
+
i = copyStart;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
i++;
|
|
112
|
+
}
|
|
113
|
+
if (copyStart === 0) return input;
|
|
114
|
+
if (copyStart < len) result += input.slice(copyStart);
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
var BOUNDARY_WHITESPACE_RE = /\s/;
|
|
118
|
+
function isAtRuleBoundary(css, idx) {
|
|
119
|
+
if (idx === 0) return true;
|
|
120
|
+
const prev = css.charCodeAt(idx - 1);
|
|
121
|
+
if (prev === 32 || prev >= 9 && prev <= 13 || prev === 59 || prev === 123 || prev === 125)
|
|
122
|
+
return true;
|
|
123
|
+
if (prev === 47 && idx >= 2 && css.charCodeAt(idx - 2) === 42) return true;
|
|
124
|
+
return prev > 127 && BOUNDARY_WHITESPACE_RE.test(css[idx - 1]);
|
|
125
|
+
}
|
|
126
|
+
var CSS_CUSTOM_IDENT_RE = /^[a-zA-Z][a-zA-Z0-9_-]*$/;
|
|
127
|
+
function codepointCompare(a, b) {
|
|
128
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
129
|
+
}
|
|
46
130
|
|
|
47
131
|
// src/directives/foundation.ts
|
|
48
132
|
var DIRECTIVE_TYPE_NAMES = [
|
|
@@ -71,7 +155,29 @@ var DIRECTIVE_TYPE_NAMES = [
|
|
|
71
155
|
"register"
|
|
72
156
|
];
|
|
73
157
|
var REMOVAL_KEY = "--ri-rm";
|
|
158
|
+
var IDENT_KEY_RE = /^[\w-]+$/;
|
|
74
159
|
var WS_CHAR_RE = /\s/;
|
|
160
|
+
function topLevelIndexOf(str, char) {
|
|
161
|
+
let depth = 0;
|
|
162
|
+
for (let i = 0; i < str.length; i++) {
|
|
163
|
+
const c = str[i];
|
|
164
|
+
if (c === '"' || c === "'") {
|
|
165
|
+
i++;
|
|
166
|
+
while (i < str.length && str[i] !== c) {
|
|
167
|
+
if (str[i] === "\\") i++;
|
|
168
|
+
i++;
|
|
169
|
+
}
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (c === "(" || c === "[") depth++;
|
|
173
|
+
else if (c === ")" || c === "]") {
|
|
174
|
+
if (depth > 0) depth--;
|
|
175
|
+
} else if (c === char && depth === 0) {
|
|
176
|
+
return i;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return -1;
|
|
180
|
+
}
|
|
75
181
|
function* scanEntries(src, opts) {
|
|
76
182
|
let i = 0;
|
|
77
183
|
while (i < src.length) {
|
|
@@ -133,7 +239,7 @@ function* scanEntries(src, opts) {
|
|
|
133
239
|
const block = src.slice(i + 1, close).trim();
|
|
134
240
|
i = close + 1;
|
|
135
241
|
if (colonIdx === -1) {
|
|
136
|
-
if (value) yield { key: "", value, fragment: true };
|
|
242
|
+
if (value) yield { key: "", value, fragment: true, block };
|
|
137
243
|
continue;
|
|
138
244
|
}
|
|
139
245
|
if (key === REMOVAL_KEY) {
|
|
@@ -155,6 +261,70 @@ function* scanEntries(src, opts) {
|
|
|
155
261
|
yield { key, value };
|
|
156
262
|
}
|
|
157
263
|
}
|
|
264
|
+
function parseKeyValueBody(body, warnings, directiveName) {
|
|
265
|
+
const entries = [];
|
|
266
|
+
const removals = [];
|
|
267
|
+
const cleanedBody = stripCSSComments(body);
|
|
268
|
+
const flush = (raw) => {
|
|
269
|
+
const line = raw.trim();
|
|
270
|
+
if (!line) return;
|
|
271
|
+
if (line.startsWith("!")) {
|
|
272
|
+
removals.push(line.slice(1).trim());
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const colonIdx = line.indexOf(":");
|
|
276
|
+
if (colonIdx === -1) return;
|
|
277
|
+
const key = line.slice(0, colonIdx).trim();
|
|
278
|
+
const value = line.slice(colonIdx + 1).trim();
|
|
279
|
+
if (key === REMOVAL_KEY && value) {
|
|
280
|
+
removals.push(value);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (key && !IDENT_KEY_RE.test(key)) {
|
|
284
|
+
warnings?.push(
|
|
285
|
+
`[RI-1035] Invalid @${directiveName ?? "directive"} entry key "${key}" \u2014 keys may only contain letters, numbers, hyphens, and underscores. The entry was skipped.`
|
|
286
|
+
);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
if (key && value) {
|
|
290
|
+
entries.push([key, value]);
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
let start = 0;
|
|
294
|
+
let depth = 0;
|
|
295
|
+
let lastNonWS = "";
|
|
296
|
+
for (let i = 0; i < cleanedBody.length; i++) {
|
|
297
|
+
const ch = cleanedBody[i];
|
|
298
|
+
if (ch === '"' || ch === "'") {
|
|
299
|
+
i++;
|
|
300
|
+
while (i < cleanedBody.length && cleanedBody[i] !== ch) {
|
|
301
|
+
if (cleanedBody[i] === "\\") i++;
|
|
302
|
+
i++;
|
|
303
|
+
}
|
|
304
|
+
lastNonWS = ch;
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
if (ch === "(" || ch === "[") {
|
|
308
|
+
depth++;
|
|
309
|
+
lastNonWS = ch;
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
if (ch === ")" || ch === "]") {
|
|
313
|
+
if (depth > 0) depth--;
|
|
314
|
+
lastNonWS = ch;
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
if (depth === 0 && (ch === ";" || ch === "\n" && lastNonWS !== ",")) {
|
|
318
|
+
flush(cleanedBody.slice(start, i));
|
|
319
|
+
start = i + 1;
|
|
320
|
+
lastNonWS = "";
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
if (!WS_CHAR_RE.test(ch)) lastNonWS = ch;
|
|
324
|
+
}
|
|
325
|
+
flush(cleanedBody.slice(start));
|
|
326
|
+
return { entries, removals };
|
|
327
|
+
}
|
|
158
328
|
function findClosingBrace(src, start) {
|
|
159
329
|
let depth = 1;
|
|
160
330
|
for (let i = start + 1; i < src.length; i++) {
|
|
@@ -191,90 +361,6 @@ function hasApplyLikeDirective(src) {
|
|
|
191
361
|
}
|
|
192
362
|
var APPLY_LIKE_MATCH_RE = new RegExp(`@(?:${APPLY_LIKE_NAMES})\\s+([^;{}]+)`, "g");
|
|
193
363
|
|
|
194
|
-
// src/shared.ts
|
|
195
|
-
function isRIDebug() {
|
|
196
|
-
return typeof process !== "undefined" && !!process.env.RI_DEBUG;
|
|
197
|
-
}
|
|
198
|
-
function withTimeout(promise, ms, message) {
|
|
199
|
-
return new Promise((resolve, reject) => {
|
|
200
|
-
const timer = setTimeout(() => reject(new Error(message)), ms);
|
|
201
|
-
if (typeof timer === "object" && "unref" in timer) timer.unref();
|
|
202
|
-
promise.then(
|
|
203
|
-
(value) => {
|
|
204
|
-
clearTimeout(timer);
|
|
205
|
-
resolve(value);
|
|
206
|
-
},
|
|
207
|
-
(err) => {
|
|
208
|
-
clearTimeout(timer);
|
|
209
|
-
reject(err);
|
|
210
|
-
}
|
|
211
|
-
);
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
function stripCSSComments(input) {
|
|
215
|
-
if (!input.includes("/")) return input;
|
|
216
|
-
const len = input.length;
|
|
217
|
-
let result = "";
|
|
218
|
-
let copyStart = 0;
|
|
219
|
-
let i = 0;
|
|
220
|
-
let quote = 0;
|
|
221
|
-
while (i < len) {
|
|
222
|
-
const c = input.charCodeAt(i);
|
|
223
|
-
if (quote !== 0) {
|
|
224
|
-
if (c === 92) {
|
|
225
|
-
i += 2;
|
|
226
|
-
continue;
|
|
227
|
-
}
|
|
228
|
-
if (c === quote) quote = 0;
|
|
229
|
-
i++;
|
|
230
|
-
continue;
|
|
231
|
-
}
|
|
232
|
-
if (c === 34 || c === 39) {
|
|
233
|
-
quote = c;
|
|
234
|
-
i++;
|
|
235
|
-
continue;
|
|
236
|
-
}
|
|
237
|
-
if (c === 47) {
|
|
238
|
-
const next = input.charCodeAt(i + 1);
|
|
239
|
-
if (next === 42) {
|
|
240
|
-
result += input.slice(copyStart, i);
|
|
241
|
-
result += " ";
|
|
242
|
-
const end = input.indexOf("*/", i + 2);
|
|
243
|
-
copyStart = end === -1 ? len : end + 2;
|
|
244
|
-
if (end === -1) break;
|
|
245
|
-
i = copyStart;
|
|
246
|
-
continue;
|
|
247
|
-
}
|
|
248
|
-
if (next === 47) {
|
|
249
|
-
result += input.slice(copyStart, i);
|
|
250
|
-
const end = input.indexOf("\n", i + 2);
|
|
251
|
-
copyStart = end === -1 ? len : end + 1;
|
|
252
|
-
if (end === -1) break;
|
|
253
|
-
result += "\n";
|
|
254
|
-
i = copyStart;
|
|
255
|
-
continue;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
i++;
|
|
259
|
-
}
|
|
260
|
-
if (copyStart === 0) return input;
|
|
261
|
-
if (copyStart < len) result += input.slice(copyStart);
|
|
262
|
-
return result;
|
|
263
|
-
}
|
|
264
|
-
var BOUNDARY_WHITESPACE_RE = /\s/;
|
|
265
|
-
function isAtRuleBoundary(css, idx) {
|
|
266
|
-
if (idx === 0) return true;
|
|
267
|
-
const prev = css.charCodeAt(idx - 1);
|
|
268
|
-
if (prev === 32 || prev >= 9 && prev <= 13 || prev === 59 || prev === 123 || prev === 125)
|
|
269
|
-
return true;
|
|
270
|
-
if (prev === 47 && idx >= 2 && css.charCodeAt(idx - 2) === 42) return true;
|
|
271
|
-
return prev > 127 && BOUNDARY_WHITESPACE_RE.test(css[idx - 1]);
|
|
272
|
-
}
|
|
273
|
-
var CSS_CUSTOM_IDENT_RE = /^[a-zA-Z][a-zA-Z0-9_-]*$/;
|
|
274
|
-
function codepointCompare(a, b) {
|
|
275
|
-
return a < b ? -1 : a > b ? 1 : 0;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
364
|
// src/directives/activation.ts
|
|
279
365
|
var DIRECTIVE_NAMES_SET = /* @__PURE__ */ new Set([
|
|
280
366
|
...DIRECTIVE_TYPE_NAMES,
|
|
@@ -445,77 +531,8 @@ function mixColorAlpha(color, alpha) {
|
|
|
445
531
|
return `color-mix(in oklab, ${color} ${alpha}, transparent)`;
|
|
446
532
|
}
|
|
447
533
|
|
|
448
|
-
// src/
|
|
449
|
-
var SAFE_FONT_FAMILY_CHARS = "a-zA-Z0-9 ._-";
|
|
450
|
-
var SAFE_FONT_FAMILY_RE = new RegExp(`^[${SAFE_FONT_FAMILY_CHARS}]+$`);
|
|
451
|
-
var GOOGLE_DEFAULT_WEIGHT = "100 900";
|
|
452
|
-
var GOOGLE_DEFAULT_STYLE = "normal italic";
|
|
453
|
-
function kindFromProvider(provider) {
|
|
454
|
-
if (provider === "google") return "google";
|
|
455
|
-
if (provider === "system") return "system";
|
|
456
|
-
if (provider === "") return "manual";
|
|
457
|
-
return "local";
|
|
458
|
-
}
|
|
459
|
-
function createFontFace(partial) {
|
|
460
|
-
const isGoogle = partial.provider === "google";
|
|
461
|
-
return {
|
|
462
|
-
weight: isGoogle ? GOOGLE_DEFAULT_WEIGHT : "400",
|
|
463
|
-
style: isGoogle ? GOOGLE_DEFAULT_STYLE : "normal",
|
|
464
|
-
display: "swap",
|
|
465
|
-
subset: "latin",
|
|
466
|
-
...partial
|
|
467
|
-
};
|
|
468
|
-
}
|
|
469
|
-
function createFontSlot(partial) {
|
|
470
|
-
const defaultProvider = partial.kind === "google" ? "google" : partial.kind === "system" ? "system" : "";
|
|
471
|
-
const faces = partial.faces && partial.faces.length > 0 ? partial.faces.map((f) => ({ ...f })) : [createFontFace({ provider: defaultProvider })];
|
|
472
|
-
return {
|
|
473
|
-
slot: partial.slot,
|
|
474
|
-
family: partial.family,
|
|
475
|
-
kind: partial.kind ?? kindFromProvider(faces[0].provider),
|
|
476
|
-
fallback: [...partial.fallback ?? []],
|
|
477
|
-
features: partial.features ?? null,
|
|
478
|
-
variation: partial.variation ?? null,
|
|
479
|
-
preload: partial.preload ?? false,
|
|
480
|
-
faces,
|
|
481
|
-
metricsFallback: partial.metricsFallback,
|
|
482
|
-
sizeAdjust: partial.sizeAdjust,
|
|
483
|
-
ascent: partial.ascent,
|
|
484
|
-
descent: partial.descent,
|
|
485
|
-
lineGap: partial.lineGap
|
|
486
|
-
};
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
// src/directives/parsers.ts
|
|
490
|
-
var UNSAFE_FONT_FAMILY_CHARS_RE = new RegExp(`[^${SAFE_FONT_FAMILY_CHARS}]`, "g");
|
|
491
|
-
var MAX_UTILITY_BODY_LENGTH = 1e4;
|
|
492
|
-
var MAX_CUSTOM_SELECTOR_LENGTH = 2e3;
|
|
534
|
+
// src/directives/color.ts
|
|
493
535
|
var MAX_COLOR_ENTRIES = 500;
|
|
494
|
-
var MAX_FONT_CONFIGS = 20;
|
|
495
|
-
var CUSTOM_VARIANT_NAME_RE = /^[a-z][\w-]*$/;
|
|
496
|
-
var IDENT_KEY_RE = /^[\w-]+$/;
|
|
497
|
-
var WS_CHAR_RE2 = /\s/;
|
|
498
|
-
function topLevelIndexOf(str, char) {
|
|
499
|
-
let depth = 0;
|
|
500
|
-
for (let i = 0; i < str.length; i++) {
|
|
501
|
-
const c = str[i];
|
|
502
|
-
if (c === '"' || c === "'") {
|
|
503
|
-
i++;
|
|
504
|
-
while (i < str.length && str[i] !== c) {
|
|
505
|
-
if (str[i] === "\\") i++;
|
|
506
|
-
i++;
|
|
507
|
-
}
|
|
508
|
-
continue;
|
|
509
|
-
}
|
|
510
|
-
if (c === "(" || c === "[") depth++;
|
|
511
|
-
else if (c === ")" || c === "]") {
|
|
512
|
-
if (depth > 0) depth--;
|
|
513
|
-
} else if (c === char && depth === 0) {
|
|
514
|
-
return i;
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
return -1;
|
|
518
|
-
}
|
|
519
536
|
function expandColorStopRef(v) {
|
|
520
537
|
if (v.includes("(") || v.startsWith("#")) return v;
|
|
521
538
|
const m = v.match(/^([\w][\w-]*)-(\d+)$/);
|
|
@@ -548,147 +565,6 @@ function expandColorSide(side) {
|
|
|
548
565
|
if (Number.isNaN(num)) return expanded;
|
|
549
566
|
return mixColorAlpha(expanded, clampAlphaPercent(num, isPercent));
|
|
550
567
|
}
|
|
551
|
-
var TRUTHY_FONT_VALUES = /* @__PURE__ */ new Set(["true", "yes", "on"]);
|
|
552
|
-
var FACE_DEFAULT_KEYS = /* @__PURE__ */ new Set([
|
|
553
|
-
"weight",
|
|
554
|
-
"style",
|
|
555
|
-
"display",
|
|
556
|
-
"subset",
|
|
557
|
-
"unicodeRange",
|
|
558
|
-
"unicode-range"
|
|
559
|
-
]);
|
|
560
|
-
function applyFaceOptions(face, entries) {
|
|
561
|
-
for (const [key, value] of entries) {
|
|
562
|
-
switch (key) {
|
|
563
|
-
case "weight":
|
|
564
|
-
face.weight = value;
|
|
565
|
-
face._weightExplicit = true;
|
|
566
|
-
break;
|
|
567
|
-
case "style":
|
|
568
|
-
face.style = value;
|
|
569
|
-
face._styleExplicit = true;
|
|
570
|
-
break;
|
|
571
|
-
case "display":
|
|
572
|
-
face.display = value;
|
|
573
|
-
break;
|
|
574
|
-
case "subset":
|
|
575
|
-
face.subset = value;
|
|
576
|
-
break;
|
|
577
|
-
case "unicodeRange":
|
|
578
|
-
case "unicode-range":
|
|
579
|
-
face.unicodeRange = value;
|
|
580
|
-
break;
|
|
581
|
-
case "preload":
|
|
582
|
-
face.preload = TRUTHY_FONT_VALUES.has(value);
|
|
583
|
-
break;
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
function applySlotOptions(slot, entries) {
|
|
588
|
-
for (const [key, value] of entries) {
|
|
589
|
-
switch (key) {
|
|
590
|
-
case "fallback":
|
|
591
|
-
slot.fallback = value.split(",").map((s) => s.trim());
|
|
592
|
-
break;
|
|
593
|
-
case "features":
|
|
594
|
-
slot.features = value;
|
|
595
|
-
break;
|
|
596
|
-
case "variation":
|
|
597
|
-
slot.variation = value;
|
|
598
|
-
break;
|
|
599
|
-
case "preload":
|
|
600
|
-
slot.preload = TRUTHY_FONT_VALUES.has(value);
|
|
601
|
-
break;
|
|
602
|
-
case "metricsFallback":
|
|
603
|
-
slot.metricsFallback = value.replace(/["']/g, "");
|
|
604
|
-
break;
|
|
605
|
-
case "sizeAdjust": {
|
|
606
|
-
const n = Number.parseFloat(value);
|
|
607
|
-
if (!Number.isNaN(n)) slot.sizeAdjust = n;
|
|
608
|
-
break;
|
|
609
|
-
}
|
|
610
|
-
case "ascent": {
|
|
611
|
-
const n = Number.parseFloat(value);
|
|
612
|
-
if (!Number.isNaN(n)) slot.ascent = n;
|
|
613
|
-
break;
|
|
614
|
-
}
|
|
615
|
-
case "descent": {
|
|
616
|
-
const n = Number.parseFloat(value);
|
|
617
|
-
if (!Number.isNaN(n)) slot.descent = n;
|
|
618
|
-
break;
|
|
619
|
-
}
|
|
620
|
-
case "lineGap": {
|
|
621
|
-
const n = Number.parseFloat(value);
|
|
622
|
-
if (!Number.isNaN(n)) slot.lineGap = n;
|
|
623
|
-
break;
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
function parseKeyValueBody(body, warnings, directiveName) {
|
|
629
|
-
const entries = [];
|
|
630
|
-
const removals = [];
|
|
631
|
-
const cleanedBody = stripCSSComments(body);
|
|
632
|
-
const flush = (raw) => {
|
|
633
|
-
const line = raw.trim();
|
|
634
|
-
if (!line) return;
|
|
635
|
-
if (line.startsWith("!")) {
|
|
636
|
-
removals.push(line.slice(1).trim());
|
|
637
|
-
return;
|
|
638
|
-
}
|
|
639
|
-
const colonIdx = line.indexOf(":");
|
|
640
|
-
if (colonIdx === -1) return;
|
|
641
|
-
const key = line.slice(0, colonIdx).trim();
|
|
642
|
-
const value = line.slice(colonIdx + 1).trim();
|
|
643
|
-
if (key === REMOVAL_KEY && value) {
|
|
644
|
-
removals.push(value);
|
|
645
|
-
return;
|
|
646
|
-
}
|
|
647
|
-
if (key && !IDENT_KEY_RE.test(key)) {
|
|
648
|
-
warnings?.push(
|
|
649
|
-
`[RI-1035] Invalid @${directiveName ?? "directive"} entry key "${key}" \u2014 keys may only contain letters, numbers, hyphens, and underscores. The entry was skipped.`
|
|
650
|
-
);
|
|
651
|
-
return;
|
|
652
|
-
}
|
|
653
|
-
if (key && value) {
|
|
654
|
-
entries.push([key, value]);
|
|
655
|
-
}
|
|
656
|
-
};
|
|
657
|
-
let start = 0;
|
|
658
|
-
let depth = 0;
|
|
659
|
-
let lastNonWS = "";
|
|
660
|
-
for (let i = 0; i < cleanedBody.length; i++) {
|
|
661
|
-
const ch = cleanedBody[i];
|
|
662
|
-
if (ch === '"' || ch === "'") {
|
|
663
|
-
i++;
|
|
664
|
-
while (i < cleanedBody.length && cleanedBody[i] !== ch) {
|
|
665
|
-
if (cleanedBody[i] === "\\") i++;
|
|
666
|
-
i++;
|
|
667
|
-
}
|
|
668
|
-
lastNonWS = ch;
|
|
669
|
-
continue;
|
|
670
|
-
}
|
|
671
|
-
if (ch === "(" || ch === "[") {
|
|
672
|
-
depth++;
|
|
673
|
-
lastNonWS = ch;
|
|
674
|
-
continue;
|
|
675
|
-
}
|
|
676
|
-
if (ch === ")" || ch === "]") {
|
|
677
|
-
if (depth > 0) depth--;
|
|
678
|
-
lastNonWS = ch;
|
|
679
|
-
continue;
|
|
680
|
-
}
|
|
681
|
-
if (depth === 0 && (ch === ";" || ch === "\n" && lastNonWS !== ",")) {
|
|
682
|
-
flush(cleanedBody.slice(start, i));
|
|
683
|
-
start = i + 1;
|
|
684
|
-
lastNonWS = "";
|
|
685
|
-
continue;
|
|
686
|
-
}
|
|
687
|
-
if (!WS_CHAR_RE2.test(ch)) lastNonWS = ch;
|
|
688
|
-
}
|
|
689
|
-
flush(cleanedBody.slice(start));
|
|
690
|
-
return { entries, removals };
|
|
691
|
-
}
|
|
692
568
|
function parseColorBody(body, warnings) {
|
|
693
569
|
const cleanedBody = stripCSSComments(body);
|
|
694
570
|
const colors = {};
|
|
@@ -759,104 +635,602 @@ function parseColorBody(body, warnings) {
|
|
|
759
635
|
`[RI-1108] @color "${key}" has an options block but its value is not generative \u2014 dark/inline/parabolic options only apply to "chroma hue" colors and were ignored.`
|
|
760
636
|
);
|
|
761
637
|
}
|
|
762
|
-
if (!Object.hasOwn(colors, key)) colorCount++;
|
|
763
|
-
colors[key] = def;
|
|
638
|
+
if (!Object.hasOwn(colors, key)) colorCount++;
|
|
639
|
+
colors[key] = def;
|
|
640
|
+
}
|
|
641
|
+
return { colors, removals };
|
|
642
|
+
}
|
|
643
|
+
function parseColorValue(key, value, warnings) {
|
|
644
|
+
const slashIdx = findPairSeparator(value);
|
|
645
|
+
if (slashIdx !== -1) {
|
|
646
|
+
const light = expandColorSide(value.slice(0, slashIdx).trim());
|
|
647
|
+
const dark = expandColorSide(value.slice(slashIdx + 1).trim());
|
|
648
|
+
return { type: "pair", light, dark };
|
|
649
|
+
}
|
|
650
|
+
if (/^light-dark\s*\(/.test(value)) {
|
|
651
|
+
const inner = value.slice(value.indexOf("(") + 1, value.lastIndexOf(")")).trim();
|
|
652
|
+
const commaIdx = topLevelIndexOf(inner, ",");
|
|
653
|
+
if (commaIdx !== -1) {
|
|
654
|
+
const light = expandColorSide(inner.slice(0, commaIdx).trim());
|
|
655
|
+
const dark = expandColorSide(inner.slice(commaIdx + 1).trim());
|
|
656
|
+
return { type: "pair", light, dark };
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
if (value === "transparent" || value === "currentColor" || value === "inherit") {
|
|
660
|
+
return { type: "keyword", value };
|
|
661
|
+
}
|
|
662
|
+
if (/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value)) {
|
|
663
|
+
return { type: "explicit", value };
|
|
664
|
+
}
|
|
665
|
+
if (/^(oklch|oklab|rgb|hsl|hwb|lch|lab|color|color-mix|var)\s*\(/.test(value)) {
|
|
666
|
+
return { type: "explicit", value };
|
|
667
|
+
}
|
|
668
|
+
const parts = value.split(/\s+/);
|
|
669
|
+
if (parts.length === 2) {
|
|
670
|
+
const chroma = Number.parseFloat(parts[0]);
|
|
671
|
+
const hue = Number.parseFloat(parts[1]);
|
|
672
|
+
if (!Number.isNaN(chroma) && !Number.isNaN(hue)) {
|
|
673
|
+
if (chroma < 0 || chroma > 0.4) {
|
|
674
|
+
warnings?.push(
|
|
675
|
+
`[RI-1102] @color "${key}" chroma ${chroma} is outside the typical range [0, 0.4] \u2014 clamping to valid range.`
|
|
676
|
+
);
|
|
677
|
+
}
|
|
678
|
+
const clampedChroma = Math.max(0, Math.min(0.4, chroma));
|
|
679
|
+
const normalizedHue = (hue % 360 + 360) % 360;
|
|
680
|
+
return { type: "generative", chroma: clampedChroma, hue: normalizedHue };
|
|
681
|
+
}
|
|
682
|
+
warnings?.push(
|
|
683
|
+
`[RI-1101] Invalid @color value "${key}: ${value}" \u2014 expected "chroma hue" (e.g., "0.15 30") or a CSS color function.`
|
|
684
|
+
);
|
|
685
|
+
return null;
|
|
686
|
+
}
|
|
687
|
+
if (parts.length === 1) {
|
|
688
|
+
const expanded = expandColorSide(value);
|
|
689
|
+
if (expanded !== value) return { type: "explicit", value: expanded };
|
|
690
|
+
if (/^[\w-]+$/.test(value)) return { type: "alias", source: value };
|
|
691
|
+
}
|
|
692
|
+
warnings?.push(
|
|
693
|
+
`[RI-1101] Invalid @color value "${key}: ${value}" \u2014 expected "chroma hue" (e.g., "0.15 30"), a color name alias, or a CSS color function.`
|
|
694
|
+
);
|
|
695
|
+
return null;
|
|
696
|
+
}
|
|
697
|
+
function parseDarkOverrideValue(value) {
|
|
698
|
+
const val = value.trim();
|
|
699
|
+
if (val === "mirror") return { strategy: "mirror" };
|
|
700
|
+
if (val === "fixed") return { strategy: "fixed" };
|
|
701
|
+
if (val.startsWith("shift")) {
|
|
702
|
+
const chromaMatch = val.match(/chroma\s+([+-]?\d+(?:\.\d+)?)/);
|
|
703
|
+
const hueMatch = val.match(/hue\s+([+-]?\d+(?:\.\d+)?)/);
|
|
704
|
+
const chromaDelta = chromaMatch ? Number.parseFloat(chromaMatch[1]) : 0;
|
|
705
|
+
const hueDelta = hueMatch ? Number.parseFloat(hueMatch[1]) : 0;
|
|
706
|
+
return {
|
|
707
|
+
strategy: "shift",
|
|
708
|
+
chromaDelta: Number.isNaN(chromaDelta) ? 0 : chromaDelta,
|
|
709
|
+
hueDelta: Number.isNaN(hueDelta) ? 0 : hueDelta
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
return void 0;
|
|
713
|
+
}
|
|
714
|
+
function findPairSeparator(value) {
|
|
715
|
+
let depth = 0;
|
|
716
|
+
for (let i = 0; i < value.length; i++) {
|
|
717
|
+
if (value[i] === "(" || value[i] === "[") depth++;
|
|
718
|
+
else if (value[i] === ")" || value[i] === "]") {
|
|
719
|
+
depth--;
|
|
720
|
+
if (depth < 0) return -1;
|
|
721
|
+
} else if (value[i] === "/" && depth === 0) {
|
|
722
|
+
const prev = value[i - 1];
|
|
723
|
+
const next = value[i + 1];
|
|
724
|
+
if (prev !== void 0 && !/\s/.test(prev) && next !== void 0 && /[\d.]/.test(next)) {
|
|
725
|
+
continue;
|
|
726
|
+
}
|
|
727
|
+
const before = value.slice(0, i).trim();
|
|
728
|
+
const after = value.slice(i + 1).trim();
|
|
729
|
+
if (before && after) {
|
|
730
|
+
return i;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
return -1;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// src/integrations/font-providers/model.ts
|
|
738
|
+
var SAFE_FONT_FAMILY_CHARS = "a-zA-Z0-9 ._-";
|
|
739
|
+
var SAFE_FONT_FAMILY_RE = new RegExp(`^[${SAFE_FONT_FAMILY_CHARS}]+$`);
|
|
740
|
+
var GOOGLE_DEFAULT_WEIGHT = "100 900";
|
|
741
|
+
var GOOGLE_DEFAULT_STYLE = "normal italic";
|
|
742
|
+
function kindFromProvider(provider) {
|
|
743
|
+
if (provider === "google") return "google";
|
|
744
|
+
if (provider === "system") return "system";
|
|
745
|
+
if (provider === "") return "manual";
|
|
746
|
+
return "local";
|
|
747
|
+
}
|
|
748
|
+
function createFontFace(partial) {
|
|
749
|
+
const isGoogle = partial.provider === "google";
|
|
750
|
+
return {
|
|
751
|
+
weight: isGoogle ? GOOGLE_DEFAULT_WEIGHT : "400",
|
|
752
|
+
style: isGoogle ? GOOGLE_DEFAULT_STYLE : "normal",
|
|
753
|
+
display: "swap",
|
|
754
|
+
...partial
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
function createFontSlot(partial) {
|
|
758
|
+
const defaultProvider = partial.kind === "google" ? "google" : partial.kind === "system" ? "system" : "";
|
|
759
|
+
const faces = partial.faces && partial.faces.length > 0 ? partial.faces.map((f) => ({ ...f })) : [createFontFace({ provider: defaultProvider })];
|
|
760
|
+
const slot = {
|
|
761
|
+
slot: partial.slot,
|
|
762
|
+
family: partial.family,
|
|
763
|
+
kind: partial.kind ?? kindFromProvider(faces[0].provider),
|
|
764
|
+
fallback: [...partial.fallback ?? []],
|
|
765
|
+
features: partial.features ?? null,
|
|
766
|
+
variation: partial.variation ?? null,
|
|
767
|
+
faces
|
|
768
|
+
};
|
|
769
|
+
if (partial.metrics !== void 0) slot.metrics = partial.metrics;
|
|
770
|
+
return slot;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// src/directives/font.ts
|
|
774
|
+
var UNSAFE_FONT_FAMILY_CHARS_RE = new RegExp(`[^${SAFE_FONT_FAMILY_CHARS}]`, "g");
|
|
775
|
+
var MAX_FONT_CONFIGS = 20;
|
|
776
|
+
var TRUTHY_FONT_VALUES = /* @__PURE__ */ new Set(["true", "yes", "on"]);
|
|
777
|
+
function stripQuotes(s) {
|
|
778
|
+
return s.replace(/["']/g, "");
|
|
779
|
+
}
|
|
780
|
+
function sanitizeFamily(family) {
|
|
781
|
+
return family.replace(UNSAFE_FONT_FAMILY_CHARS_RE, "");
|
|
782
|
+
}
|
|
783
|
+
function sanitizeFallbacks(parts) {
|
|
784
|
+
return parts.map((s) => SAFE_FONT_FAMILY_RE.test(s) ? s : sanitizeFamily(s).trim()).filter((s) => s.length > 0);
|
|
785
|
+
}
|
|
786
|
+
function isUnsafeCSSValue(value) {
|
|
787
|
+
if (value.includes("\0")) return true;
|
|
788
|
+
for (let i = 0; i < value.length; i++) {
|
|
789
|
+
const c = value[i];
|
|
790
|
+
if (c === '"' || c === "'") {
|
|
791
|
+
i++;
|
|
792
|
+
while (i < value.length && value[i] !== c) {
|
|
793
|
+
if (value[i] === "\\") i++;
|
|
794
|
+
i++;
|
|
795
|
+
}
|
|
796
|
+
if (i >= value.length) return true;
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
799
|
+
if (c === "}") return true;
|
|
800
|
+
}
|
|
801
|
+
return false;
|
|
802
|
+
}
|
|
803
|
+
function dropUnsafeValue(key, value, slot, warnings) {
|
|
804
|
+
if (!isUnsafeCSSValue(value)) return false;
|
|
805
|
+
warnings?.push(
|
|
806
|
+
`[RI-1217] @font option "${key}" in slot "${slot}" has a value that can't be emitted safely into CSS \u2014 the entry was ignored.`
|
|
807
|
+
);
|
|
808
|
+
return true;
|
|
809
|
+
}
|
|
810
|
+
function warnDeprecated(warnings, slot, oldForm, replacement) {
|
|
811
|
+
warnings?.push(
|
|
812
|
+
`[RI-1218] @font slot "${slot}": ${oldForm} is deprecated \u2014 ${replacement}. The old form still works but will be removed.`
|
|
813
|
+
);
|
|
814
|
+
}
|
|
815
|
+
function topLevelFromIndex(s) {
|
|
816
|
+
let last = -1;
|
|
817
|
+
for (let i = 0; i < s.length; i++) {
|
|
818
|
+
const c = s[i];
|
|
819
|
+
if (c === '"' || c === "'") {
|
|
820
|
+
i++;
|
|
821
|
+
while (i < s.length && s[i] !== c) {
|
|
822
|
+
if (s[i] === "\\") i++;
|
|
823
|
+
i++;
|
|
824
|
+
}
|
|
825
|
+
continue;
|
|
826
|
+
}
|
|
827
|
+
if (c === "f" && i > 0 && s[i - 1] === " " && s.startsWith("from ", i)) last = i;
|
|
828
|
+
}
|
|
829
|
+
return last;
|
|
830
|
+
}
|
|
831
|
+
function splitTopLevelCommas(s) {
|
|
832
|
+
const parts = [];
|
|
833
|
+
let rest = s;
|
|
834
|
+
for (; ; ) {
|
|
835
|
+
const i = topLevelIndexOf(rest, ",");
|
|
836
|
+
if (i === -1) {
|
|
837
|
+
parts.push(rest);
|
|
838
|
+
return parts;
|
|
839
|
+
}
|
|
840
|
+
parts.push(rest.slice(0, i));
|
|
841
|
+
rest = rest.slice(i + 1);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
function parsePreamble(text, slot, warnings) {
|
|
845
|
+
const pre = text.replace(/\s+/g, " ").trim();
|
|
846
|
+
if (pre === "system") return { system: true, family: "", fallback: [] };
|
|
847
|
+
let stackText = pre;
|
|
848
|
+
let provider;
|
|
849
|
+
const fromIdx = topLevelFromIndex(pre);
|
|
850
|
+
if (fromIdx !== -1) {
|
|
851
|
+
const tail = stripQuotes(pre.slice(fromIdx + 4).trim());
|
|
852
|
+
if (tail) {
|
|
853
|
+
provider = tail;
|
|
854
|
+
stackText = pre.slice(0, fromIdx).trim();
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
const parts = splitTopLevelCommas(stackText).map((s) => stripQuotes(s.trim()).trim());
|
|
858
|
+
const family = parts[0] ?? "";
|
|
859
|
+
const fallback = sanitizeFallbacks(parts.slice(1));
|
|
860
|
+
if (provider === void 0 || provider === "google") {
|
|
861
|
+
return provider === "google" ? { family, fallback, google: true } : { family, fallback };
|
|
862
|
+
}
|
|
863
|
+
if (provider === "system") {
|
|
864
|
+
warnDeprecated(warnings, slot, "`from system`", "use the bare `system` keyword");
|
|
865
|
+
return { system: true, family: "", fallback: [] };
|
|
866
|
+
}
|
|
867
|
+
warnDeprecated(
|
|
868
|
+
warnings,
|
|
869
|
+
slot,
|
|
870
|
+
`\`from "${provider}"\``,
|
|
871
|
+
`declare the file as a face entry (\`face: ${provider};\`)`
|
|
872
|
+
);
|
|
873
|
+
return { family, fallback, legacyFaceSrc: provider };
|
|
874
|
+
}
|
|
875
|
+
function parseMetricsValue(value, slot, warnings) {
|
|
876
|
+
if (value === "none") return null;
|
|
877
|
+
const tokens = value.match(/"[^"]*"|'[^']*'|\S+/g) ?? [];
|
|
878
|
+
const familyParts = [];
|
|
879
|
+
const nums = [];
|
|
880
|
+
let malformed = false;
|
|
881
|
+
for (const t of tokens) {
|
|
882
|
+
const n = Number.parseFloat(t);
|
|
883
|
+
if (Number.isNaN(n)) {
|
|
884
|
+
if (nums.length > 0) {
|
|
885
|
+
malformed = true;
|
|
886
|
+
break;
|
|
887
|
+
}
|
|
888
|
+
familyParts.push(stripQuotes(t));
|
|
889
|
+
} else {
|
|
890
|
+
nums.push(n);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
const fallbackName = familyParts.join(" ").trim();
|
|
894
|
+
if (malformed || nums.length !== 0 && nums.length !== 4 || familyParts.length > 0 && fallbackName === "") {
|
|
895
|
+
warnings?.push(
|
|
896
|
+
`[RI-1220] @font slot "${slot}" has an invalid metrics value "${value}" \u2014 use \`metrics: none\`, \`metrics: "<local font>"\`, or \`metrics: "<local font>" <size-adjust> <ascent> <descent> <line-gap>\` (four percentages). The entry was ignored.`
|
|
897
|
+
);
|
|
898
|
+
return void 0;
|
|
899
|
+
}
|
|
900
|
+
const cfg = {};
|
|
901
|
+
if (fallbackName) cfg.fallback = fallbackName;
|
|
902
|
+
if (nums.length === 4) {
|
|
903
|
+
cfg.sizeAdjust = nums[0];
|
|
904
|
+
cfg.ascent = nums[1];
|
|
905
|
+
cfg.descent = nums[2];
|
|
906
|
+
cfg.lineGap = nums[3];
|
|
907
|
+
}
|
|
908
|
+
return cfg;
|
|
909
|
+
}
|
|
910
|
+
function normalizeFaceEntries(entries, slot, warnings) {
|
|
911
|
+
const out = [];
|
|
912
|
+
for (const [key, value] of entries) {
|
|
913
|
+
if (key === "unicodeRange") {
|
|
914
|
+
warnDeprecated(warnings, slot, "`unicodeRange:`", "use the CSS spelling `unicode-range:`");
|
|
915
|
+
out.push(["unicode-range", value]);
|
|
916
|
+
} else if (key === "subset") {
|
|
917
|
+
warnDeprecated(
|
|
918
|
+
warnings,
|
|
919
|
+
slot,
|
|
920
|
+
"`subset:`",
|
|
921
|
+
"remove the entry (the Google css2 API takes no subset hint)"
|
|
922
|
+
);
|
|
923
|
+
} else {
|
|
924
|
+
out.push([key, value]);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
return out;
|
|
928
|
+
}
|
|
929
|
+
function applyFaceOptions(face, entries, slot, warnings) {
|
|
930
|
+
for (const [key, value] of entries) {
|
|
931
|
+
if (dropUnsafeValue(key, value, slot, warnings)) continue;
|
|
932
|
+
switch (key) {
|
|
933
|
+
case "weight":
|
|
934
|
+
face.weight = value;
|
|
935
|
+
face._weightExplicit = true;
|
|
936
|
+
break;
|
|
937
|
+
case "style":
|
|
938
|
+
face.style = value;
|
|
939
|
+
face._styleExplicit = true;
|
|
940
|
+
break;
|
|
941
|
+
case "display":
|
|
942
|
+
face.display = value;
|
|
943
|
+
break;
|
|
944
|
+
case "unicode-range":
|
|
945
|
+
face.unicodeRange = value;
|
|
946
|
+
break;
|
|
947
|
+
case "preload":
|
|
948
|
+
face.preload = TRUTHY_FONT_VALUES.has(value);
|
|
949
|
+
break;
|
|
950
|
+
case "src":
|
|
951
|
+
warnings?.push(
|
|
952
|
+
`[RI-1217] Unknown @font option "src" in slot "${slot}" \u2014 the face source is the face: value itself (\`face: /path.woff2 { \u2026 }\`). The entry was ignored.`
|
|
953
|
+
);
|
|
954
|
+
break;
|
|
955
|
+
default:
|
|
956
|
+
warnings?.push(
|
|
957
|
+
`[RI-1217] Unknown @font option "${key}" in slot "${slot}" \u2014 the entry was ignored.`
|
|
958
|
+
);
|
|
959
|
+
break;
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
var LEGACY_METRICS_KEYS = /* @__PURE__ */ new Set([
|
|
964
|
+
"metricsFallback",
|
|
965
|
+
"sizeAdjust",
|
|
966
|
+
"ascent",
|
|
967
|
+
"descent",
|
|
968
|
+
"lineGap"
|
|
969
|
+
]);
|
|
970
|
+
function parseSlotBody(body, slot, warnings) {
|
|
971
|
+
const out = { faceDefaults: [], faces: [], legacyMetrics: [] };
|
|
972
|
+
for (const entry of scanEntries(body, { newlineTerminates: true })) {
|
|
973
|
+
if (entry.unclosedBlock) {
|
|
974
|
+
warnings?.push(
|
|
975
|
+
`[RI-1217] @font slot "${slot}" has an unterminated { block after "${entry.key || entry.value}" \u2014 the entry was ignored.`
|
|
976
|
+
);
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
979
|
+
if (entry.fragment) {
|
|
980
|
+
if (entry.value === "@face" && entry.block !== void 0) {
|
|
981
|
+
warnDeprecated(warnings, slot, "`@face { src: \u2026; }`", "use `face: <src> { \u2026 }`");
|
|
982
|
+
let src = "";
|
|
983
|
+
const own = [];
|
|
984
|
+
for (const [k, v] of parseKeyValueBody(entry.block, warnings, "font").entries) {
|
|
985
|
+
if (k === "src") src = stripQuotes(v);
|
|
986
|
+
else own.push([k, v]);
|
|
987
|
+
}
|
|
988
|
+
out.faces.push({ src, entries: own });
|
|
989
|
+
} else if (entry.value) {
|
|
990
|
+
warnings?.push(
|
|
991
|
+
`[RI-1217] @font slot "${slot}" has a stray value "${entry.value.slice(0, 60)}" with no key \u2014 if it continues the previous entry's value, keep the entry on one line. The text was ignored.`
|
|
992
|
+
);
|
|
993
|
+
}
|
|
994
|
+
continue;
|
|
995
|
+
}
|
|
996
|
+
if (entry.removal || !entry.key) continue;
|
|
997
|
+
if (!IDENT_KEY_RE.test(entry.key)) {
|
|
998
|
+
warnings?.push(
|
|
999
|
+
`[RI-1035] Invalid @font entry key "${entry.key}" \u2014 keys may only contain letters, numbers, hyphens, and underscores. The entry was skipped.`
|
|
1000
|
+
);
|
|
1001
|
+
continue;
|
|
1002
|
+
}
|
|
1003
|
+
if (entry.block !== void 0 && entry.key !== "face") {
|
|
1004
|
+
warnings?.push(
|
|
1005
|
+
`[RI-1217] @font option "${entry.key}" in slot "${slot}" takes no { \u2026 } block \u2014 the block was ignored.`
|
|
1006
|
+
);
|
|
1007
|
+
}
|
|
1008
|
+
if (entry.key === "face" && !entry.value) {
|
|
1009
|
+
warnings?.push(
|
|
1010
|
+
`[RI-1217] @font slot "${slot}" has a face: entry with no source \u2014 use \`face: <src> [{ \u2026 }]\`. The entry was ignored.`
|
|
1011
|
+
);
|
|
1012
|
+
continue;
|
|
1013
|
+
}
|
|
1014
|
+
if (!entry.value) continue;
|
|
1015
|
+
if (LEGACY_METRICS_KEYS.has(entry.key)) {
|
|
1016
|
+
out.legacyMetrics.push([entry.key, entry.value]);
|
|
1017
|
+
continue;
|
|
1018
|
+
}
|
|
1019
|
+
switch (entry.key) {
|
|
1020
|
+
case "face":
|
|
1021
|
+
out.faces.push({
|
|
1022
|
+
src: stripQuotes(entry.value),
|
|
1023
|
+
entries: entry.block !== void 0 ? parseKeyValueBody(entry.block, warnings, "font").entries : []
|
|
1024
|
+
});
|
|
1025
|
+
break;
|
|
1026
|
+
case "features":
|
|
1027
|
+
if (!dropUnsafeValue(entry.key, entry.value, slot, warnings)) out.features = entry.value;
|
|
1028
|
+
break;
|
|
1029
|
+
case "variation":
|
|
1030
|
+
if (!dropUnsafeValue(entry.key, entry.value, slot, warnings)) out.variation = entry.value;
|
|
1031
|
+
break;
|
|
1032
|
+
case "metrics": {
|
|
1033
|
+
const cfg = parseMetricsValue(entry.value, slot, warnings);
|
|
1034
|
+
if (cfg !== void 0) out.metrics = cfg;
|
|
1035
|
+
break;
|
|
1036
|
+
}
|
|
1037
|
+
case "weight":
|
|
1038
|
+
case "style":
|
|
1039
|
+
case "display":
|
|
1040
|
+
case "unicode-range":
|
|
1041
|
+
case "preload":
|
|
1042
|
+
out.faceDefaults.push([entry.key, entry.value]);
|
|
1043
|
+
break;
|
|
1044
|
+
case "italic":
|
|
1045
|
+
warnDeprecated(warnings, slot, "`italic: <src>`", "use `face: <src> { style: italic; }`");
|
|
1046
|
+
out.faces.push({ src: stripQuotes(entry.value), entries: [["style", "italic"]] });
|
|
1047
|
+
break;
|
|
1048
|
+
case "fallback":
|
|
1049
|
+
warnDeprecated(
|
|
1050
|
+
warnings,
|
|
1051
|
+
slot,
|
|
1052
|
+
"`fallback:`",
|
|
1053
|
+
'list fallbacks after the family in the slot preamble (`sans: "Inter", ui-sans-serif from google;`)'
|
|
1054
|
+
);
|
|
1055
|
+
out.fallbackOverride = sanitizeFallbacks(
|
|
1056
|
+
splitTopLevelCommas(entry.value).map((s) => stripQuotes(s.trim()).trim())
|
|
1057
|
+
);
|
|
1058
|
+
break;
|
|
1059
|
+
case "unicodeRange":
|
|
1060
|
+
warnDeprecated(warnings, slot, "`unicodeRange:`", "use the CSS spelling `unicode-range:`");
|
|
1061
|
+
out.faceDefaults.push(["unicode-range", entry.value]);
|
|
1062
|
+
break;
|
|
1063
|
+
case "subset":
|
|
1064
|
+
warnDeprecated(
|
|
1065
|
+
warnings,
|
|
1066
|
+
slot,
|
|
1067
|
+
"`subset:`",
|
|
1068
|
+
"remove the entry (the Google css2 API takes no subset hint)"
|
|
1069
|
+
);
|
|
1070
|
+
break;
|
|
1071
|
+
default:
|
|
1072
|
+
warnings?.push(
|
|
1073
|
+
`[RI-1217] Unknown @font option "${entry.key}" in slot "${slot}" \u2014 the entry was ignored.`
|
|
1074
|
+
);
|
|
1075
|
+
break;
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
foldLegacyMetrics(out, slot, warnings);
|
|
1079
|
+
return out;
|
|
1080
|
+
}
|
|
1081
|
+
function foldLegacyMetrics(body, slot, warnings) {
|
|
1082
|
+
if (body.legacyMetrics.length === 0) return;
|
|
1083
|
+
warnDeprecated(
|
|
1084
|
+
warnings,
|
|
1085
|
+
slot,
|
|
1086
|
+
"the metricsFallback/sizeAdjust/ascent/descent/lineGap keys",
|
|
1087
|
+
'use the single `metrics:` key (e.g. `metrics: "Arial" 107.64 90.49 22.48 0;`) or omit it for automatic metrics'
|
|
1088
|
+
);
|
|
1089
|
+
if (body.metrics !== void 0) return;
|
|
1090
|
+
const cfg = {};
|
|
1091
|
+
for (const [key, value] of body.legacyMetrics) {
|
|
1092
|
+
if (key === "metricsFallback") {
|
|
1093
|
+
cfg.fallback = stripQuotes(value);
|
|
1094
|
+
} else {
|
|
1095
|
+
const n = Number.parseFloat(value);
|
|
1096
|
+
if (!Number.isNaN(n)) cfg[key] = n;
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
const numeric = [cfg.sizeAdjust, cfg.ascent, cfg.descent, cfg.lineGap].filter(
|
|
1100
|
+
(n) => n !== void 0
|
|
1101
|
+
).length;
|
|
1102
|
+
if (numeric === 4 || numeric === 0 && cfg.fallback !== void 0) {
|
|
1103
|
+
body.metrics = cfg;
|
|
1104
|
+
} else if (numeric > 0) {
|
|
1105
|
+
warnings?.push(
|
|
1106
|
+
`[RI-1220] @font slot "${slot}" sets only ${numeric} of the four metric overrides \u2014 size-adjust, ascent, descent, and line-gap are all required. The values were ignored (automatic metrics still apply).`
|
|
1107
|
+
);
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
function buildFace(provider, defaults, slot, warnings, own) {
|
|
1111
|
+
const face = createFontFace({ provider });
|
|
1112
|
+
applyFaceOptions(face, defaults, slot, warnings);
|
|
1113
|
+
if (own) applyFaceOptions(face, own, slot, warnings);
|
|
1114
|
+
return face;
|
|
1115
|
+
}
|
|
1116
|
+
function warnDuplicateFaces(slot, warnings) {
|
|
1117
|
+
if (!warnings || slot.faces.length < 2) return;
|
|
1118
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1119
|
+
for (const face of slot.faces) {
|
|
1120
|
+
const key = `${face.weight}|${face.style}`;
|
|
1121
|
+
if (seen.has(key)) {
|
|
1122
|
+
warnings.push(
|
|
1123
|
+
`[RI-1214] @font slot "${slot.slot}" has duplicate faces with weight "${face.weight}" and style "${face.style}" \u2014 the later @font-face wins. Remove the duplicate or give it a distinct weight/style.`
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
seen.add(key);
|
|
764
1127
|
}
|
|
765
|
-
return { colors, removals };
|
|
766
1128
|
}
|
|
767
|
-
function
|
|
768
|
-
const
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
const commaIdx = topLevelIndexOf(inner, ",");
|
|
777
|
-
if (commaIdx !== -1) {
|
|
778
|
-
const light = expandColorSide(inner.slice(0, commaIdx).trim());
|
|
779
|
-
const dark = expandColorSide(inner.slice(commaIdx + 1).trim());
|
|
780
|
-
return { type: "pair", light, dark };
|
|
1129
|
+
function buildSlot(slot, preambleText, blockBody, warnings) {
|
|
1130
|
+
const pre = parsePreamble(preambleText, slot, warnings);
|
|
1131
|
+
const body = parseSlotBody(blockBody ?? "", slot, warnings);
|
|
1132
|
+
const preloadDefault = body.faceDefaults.some(([k]) => k === "preload");
|
|
1133
|
+
if (pre.system) {
|
|
1134
|
+
if (preloadDefault) {
|
|
1135
|
+
warnings?.push(
|
|
1136
|
+
`[RI-1219] @font slot "${slot}": preload has no effect on system fonts \u2014 only local font files can be preloaded.`
|
|
1137
|
+
);
|
|
781
1138
|
}
|
|
1139
|
+
return createFontSlot({ slot, family: "", kind: "system" });
|
|
782
1140
|
}
|
|
783
|
-
if (
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
1141
|
+
if (!SAFE_FONT_FAMILY_RE.test(pre.family)) {
|
|
1142
|
+
if (pre.family === "") {
|
|
1143
|
+
warnings?.push(
|
|
1144
|
+
`[RI-1217] @font slot "${slot}" has no font family \u2014 the slot emits an empty font variable.`
|
|
1145
|
+
);
|
|
1146
|
+
}
|
|
1147
|
+
return createFontSlot({ slot, family: sanitizeFamily(pre.family), kind: "manual" });
|
|
788
1148
|
}
|
|
789
|
-
|
|
790
|
-
|
|
1149
|
+
const srcFaces = pre.legacyFaceSrc ? [{ src: pre.legacyFaceSrc, entries: [] }, ...body.faces] : body.faces;
|
|
1150
|
+
let config;
|
|
1151
|
+
if (pre.google) {
|
|
1152
|
+
if (srcFaces.length > 0) {
|
|
1153
|
+
warnings?.push(
|
|
1154
|
+
`[RI-1204] @font slot "${slot}" loads "${pre.family}" from google but also declares local face entries \u2014 provider fonts can't be combined with local faces. The extra faces were ignored.`
|
|
1155
|
+
);
|
|
1156
|
+
}
|
|
1157
|
+
config = createFontSlot({
|
|
1158
|
+
slot,
|
|
1159
|
+
family: pre.family,
|
|
1160
|
+
kind: "google",
|
|
1161
|
+
fallback: pre.fallback,
|
|
1162
|
+
faces: [buildFace("google", body.faceDefaults, slot, warnings)]
|
|
1163
|
+
});
|
|
1164
|
+
} else if (srcFaces.length > 0) {
|
|
1165
|
+
config = createFontSlot({
|
|
1166
|
+
slot,
|
|
1167
|
+
family: pre.family,
|
|
1168
|
+
kind: "local",
|
|
1169
|
+
fallback: pre.fallback,
|
|
1170
|
+
faces: srcFaces.map(
|
|
1171
|
+
(f) => buildFace(
|
|
1172
|
+
f.src,
|
|
1173
|
+
body.faceDefaults,
|
|
1174
|
+
slot,
|
|
1175
|
+
warnings,
|
|
1176
|
+
normalizeFaceEntries(f.entries, slot, warnings)
|
|
1177
|
+
)
|
|
1178
|
+
)
|
|
1179
|
+
});
|
|
1180
|
+
warnDuplicateFaces(config, warnings);
|
|
1181
|
+
} else {
|
|
1182
|
+
config = createFontSlot({
|
|
1183
|
+
slot,
|
|
1184
|
+
family: pre.family,
|
|
1185
|
+
kind: "manual",
|
|
1186
|
+
fallback: pre.fallback,
|
|
1187
|
+
faces: [buildFace("", body.faceDefaults, slot, warnings)]
|
|
1188
|
+
});
|
|
791
1189
|
}
|
|
792
|
-
|
|
793
|
-
if (
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
if (
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
);
|
|
801
|
-
}
|
|
802
|
-
const clampedChroma = Math.max(0, Math.min(0.4, chroma));
|
|
803
|
-
const normalizedHue = (hue % 360 + 360) % 360;
|
|
804
|
-
return { type: "generative", chroma: clampedChroma, hue: normalizedHue };
|
|
1190
|
+
if (body.fallbackOverride) config.fallback = body.fallbackOverride;
|
|
1191
|
+
if (body.features !== void 0) config.features = body.features;
|
|
1192
|
+
if (body.variation !== void 0) config.variation = body.variation;
|
|
1193
|
+
if (body.metrics !== void 0) {
|
|
1194
|
+
if (config.kind === "manual") {
|
|
1195
|
+
warnings?.push(
|
|
1196
|
+
`[RI-1220] @font slot "${slot}" sets metrics on a manual font stack \u2014 metrics apply to loaded (google or local file) fonts only. The entry has no effect.`
|
|
1197
|
+
);
|
|
805
1198
|
}
|
|
1199
|
+
config.metrics = body.metrics;
|
|
1200
|
+
}
|
|
1201
|
+
if (config.kind !== "local" && preloadDefault) {
|
|
806
1202
|
warnings?.push(
|
|
807
|
-
`[RI-
|
|
1203
|
+
`[RI-1219] @font slot "${slot}": preload has no effect on ${config.kind} fonts \u2014 only local font files can be preloaded.`
|
|
808
1204
|
);
|
|
809
|
-
return null;
|
|
810
|
-
}
|
|
811
|
-
if (parts.length === 1) {
|
|
812
|
-
const expanded = expandColorSide(value);
|
|
813
|
-
if (expanded !== value) return { type: "explicit", value: expanded };
|
|
814
|
-
if (/^[\w-]+$/.test(value)) return { type: "alias", source: value };
|
|
815
|
-
}
|
|
816
|
-
warnings?.push(
|
|
817
|
-
`[RI-1101] Invalid @color value "${key}: ${value}" \u2014 expected "chroma hue" (e.g., "0.15 30"), a color name alias, or a CSS color function.`
|
|
818
|
-
);
|
|
819
|
-
return null;
|
|
820
|
-
}
|
|
821
|
-
function parseDarkOverrideValue(value) {
|
|
822
|
-
const val = value.trim();
|
|
823
|
-
if (val === "mirror") return { strategy: "mirror" };
|
|
824
|
-
if (val === "fixed") return { strategy: "fixed" };
|
|
825
|
-
if (val.startsWith("shift")) {
|
|
826
|
-
const chromaMatch = val.match(/chroma\s+([+-]?\d+(?:\.\d+)?)/);
|
|
827
|
-
const hueMatch = val.match(/hue\s+([+-]?\d+(?:\.\d+)?)/);
|
|
828
|
-
const chromaDelta = chromaMatch ? Number.parseFloat(chromaMatch[1]) : 0;
|
|
829
|
-
const hueDelta = hueMatch ? Number.parseFloat(hueMatch[1]) : 0;
|
|
830
|
-
return {
|
|
831
|
-
strategy: "shift",
|
|
832
|
-
chromaDelta: Number.isNaN(chromaDelta) ? 0 : chromaDelta,
|
|
833
|
-
hueDelta: Number.isNaN(hueDelta) ? 0 : hueDelta
|
|
834
|
-
};
|
|
835
1205
|
}
|
|
836
|
-
return
|
|
1206
|
+
return config;
|
|
837
1207
|
}
|
|
838
|
-
function
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
}
|
|
1208
|
+
function parseNestedFontBlock(body, warnings) {
|
|
1209
|
+
const cleanedBody = stripCSSComments(body);
|
|
1210
|
+
const configs = [];
|
|
1211
|
+
for (const entry of scanEntries(cleanedBody, { newlineTerminates: false })) {
|
|
1212
|
+
if (entry.removal || entry.fragment) continue;
|
|
1213
|
+
if (entry.unclosedBlock) break;
|
|
1214
|
+
if (!entry.key) continue;
|
|
1215
|
+
if (configs.length >= MAX_FONT_CONFIGS) {
|
|
1216
|
+
warnings?.push(
|
|
1217
|
+
`[RI-1216] @font block exceeds ${MAX_FONT_CONFIGS} slot definitions \u2014 the remaining slots were skipped. Split rarely-used slots into a separate stylesheet or remove unused ones.`
|
|
1218
|
+
);
|
|
1219
|
+
break;
|
|
1220
|
+
}
|
|
1221
|
+
if (entry.block !== void 0) {
|
|
1222
|
+
configs.push(buildSlot(entry.key, entry.value, entry.block, warnings));
|
|
1223
|
+
} else if (entry.value) {
|
|
1224
|
+
configs.push(buildSlot(entry.key, entry.value, void 0, warnings));
|
|
856
1225
|
}
|
|
857
1226
|
}
|
|
858
|
-
return
|
|
1227
|
+
return configs;
|
|
859
1228
|
}
|
|
1229
|
+
|
|
1230
|
+
// src/directives/parsers.ts
|
|
1231
|
+
var MAX_UTILITY_BODY_LENGTH = 1e4;
|
|
1232
|
+
var MAX_CUSTOM_SELECTOR_LENGTH = 2e3;
|
|
1233
|
+
var CUSTOM_VARIANT_NAME_RE = /^[a-z][\w-]*$/;
|
|
860
1234
|
function parseTextBody(body, warnings) {
|
|
861
1235
|
const { entries, removals } = parseKeyValueBody(body, warnings, "text");
|
|
862
1236
|
const text = {};
|
|
@@ -941,191 +1315,18 @@ function parsePreflightDirective(body, modifier, base) {
|
|
|
941
1315
|
}
|
|
942
1316
|
return config;
|
|
943
1317
|
}
|
|
944
|
-
function
|
|
945
|
-
return
|
|
946
|
-
}
|
|
947
|
-
function buildFace(provider, faceDefaults, forceStyle) {
|
|
948
|
-
const face = createFontFace({ provider });
|
|
949
|
-
applyFaceOptions(face, faceDefaults);
|
|
950
|
-
if (forceStyle !== void 0) {
|
|
951
|
-
face.style = forceStyle;
|
|
952
|
-
face._styleExplicit = true;
|
|
953
|
-
}
|
|
954
|
-
return face;
|
|
955
|
-
}
|
|
956
|
-
function faceFromBlock(entries, faceDefaults) {
|
|
957
|
-
let src = "";
|
|
958
|
-
const own = [];
|
|
959
|
-
for (const [k, v] of entries) {
|
|
960
|
-
if (k === "src") src = v.replace(/["']/g, "");
|
|
961
|
-
else own.push([k, v]);
|
|
962
|
-
}
|
|
963
|
-
const face = createFontFace({ provider: src });
|
|
964
|
-
applyFaceOptions(face, faceDefaults);
|
|
965
|
-
applyFaceOptions(face, own);
|
|
966
|
-
return face;
|
|
967
|
-
}
|
|
968
|
-
function extractFaceBlocks(body, warnings) {
|
|
969
|
-
const faceBlocks = [];
|
|
970
|
-
let rest = "";
|
|
971
|
-
let i = 0;
|
|
972
|
-
while (i < body.length) {
|
|
973
|
-
const at = body.indexOf("@face", i);
|
|
974
|
-
if (at === -1) {
|
|
975
|
-
rest += body.slice(i);
|
|
976
|
-
break;
|
|
977
|
-
}
|
|
978
|
-
rest += body.slice(i, at);
|
|
979
|
-
let j = at + 5;
|
|
980
|
-
while (j < body.length && /\s/.test(body[j])) j++;
|
|
981
|
-
if (body[j] !== "{") {
|
|
982
|
-
rest += body.slice(at, j);
|
|
983
|
-
i = j;
|
|
984
|
-
continue;
|
|
985
|
-
}
|
|
986
|
-
const close = findClosingBrace(body, j);
|
|
987
|
-
if (close === -1) {
|
|
988
|
-
rest += body.slice(at);
|
|
989
|
-
break;
|
|
990
|
-
}
|
|
991
|
-
faceBlocks.push(parseKeyValueBody(body.slice(j + 1, close), warnings, "font").entries);
|
|
992
|
-
i = close + 1;
|
|
993
|
-
}
|
|
994
|
-
return { faceBlocks, rest };
|
|
995
|
-
}
|
|
996
|
-
function processSlotBlock(blockBody, warnings) {
|
|
997
|
-
const { faceBlocks, rest } = extractFaceBlocks(blockBody, warnings);
|
|
998
|
-
const { entries } = parseKeyValueBody(rest, warnings, "font");
|
|
999
|
-
const faceDefaults = [];
|
|
1000
|
-
const slotOpts = [];
|
|
1001
|
-
const italicSrcs = [];
|
|
1002
|
-
for (const [k, v] of entries) {
|
|
1003
|
-
if (k === "italic") italicSrcs.push(v.replace(/["']/g, ""));
|
|
1004
|
-
else if (FACE_DEFAULT_KEYS.has(k)) faceDefaults.push([k, v]);
|
|
1005
|
-
else slotOpts.push([k, v]);
|
|
1006
|
-
}
|
|
1007
|
-
return { faceDefaults, slotOpts, faceBlocks, italicSrcs };
|
|
1008
|
-
}
|
|
1009
|
-
function buildFaces(primaryProvider, block) {
|
|
1010
|
-
const faces = [];
|
|
1011
|
-
if (primaryProvider !== null) faces.push(buildFace(primaryProvider, block.faceDefaults));
|
|
1012
|
-
for (const fb of block.faceBlocks) faces.push(faceFromBlock(fb, block.faceDefaults));
|
|
1013
|
-
for (const src of block.italicSrcs) faces.push(buildFace(src, block.faceDefaults, "italic"));
|
|
1014
|
-
if (faces.length === 0) faces.push(buildFace("", block.faceDefaults));
|
|
1015
|
-
return faces;
|
|
1016
|
-
}
|
|
1017
|
-
function warnDuplicateFaces(slot, warnings) {
|
|
1018
|
-
if (!warnings || slot.faces.length < 2) return;
|
|
1019
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1020
|
-
for (const face of slot.faces) {
|
|
1021
|
-
const key = `${face.weight}|${face.style}`;
|
|
1022
|
-
if (seen.has(key)) {
|
|
1023
|
-
warnings.push(
|
|
1024
|
-
`[RI-1214] @font slot "${slot.slot}" has duplicate faces with weight "${face.weight}" and style "${face.style}" \u2014 the later @font-face wins. Remove the duplicate or give it a distinct weight/style.`
|
|
1025
|
-
);
|
|
1026
|
-
}
|
|
1027
|
-
seen.add(key);
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
function parseFontBody(body, slot, warnings) {
|
|
1031
|
-
const normalizedBody = stripCSSComments(body).trim();
|
|
1032
|
-
if (normalizedBody === "system") {
|
|
1033
|
-
return createFontSlot({ slot, family: "", kind: "system" });
|
|
1034
|
-
}
|
|
1035
|
-
const braceIdx = topLevelIndexOf(normalizedBody, "{");
|
|
1036
|
-
const matchable = braceIdx === -1 ? normalizedBody.replace(/\s+/g, " ") : normalizedBody.slice(0, braceIdx).replace(/\s+/g, " ") + normalizedBody.slice(braceIdx);
|
|
1037
|
-
const fromMatch = matchable.match(/^(["'])(.+?)\1\s+from\s+(.+?)(?:\s*\{([\s\S]*)\})?$/);
|
|
1038
|
-
if (fromMatch) {
|
|
1039
|
-
const family = fromMatch[2];
|
|
1040
|
-
if (!SAFE_FONT_FAMILY_RE.test(family)) {
|
|
1041
|
-
return createFontSlot({ slot, family: sanitizeFamily(family), kind: "manual" });
|
|
1042
|
-
}
|
|
1043
|
-
const provider = fromMatch[3].trim().replace(/["']/g, "");
|
|
1044
|
-
const kind = kindFromProvider(provider);
|
|
1045
|
-
const block = processSlotBlock(fromMatch[4] || "", warnings);
|
|
1046
|
-
if (kind === "google" || kind === "system") {
|
|
1047
|
-
if (block.faceBlocks.length > 0 || block.italicSrcs.length > 0) {
|
|
1048
|
-
warnings?.push(
|
|
1049
|
-
`[RI-1204] @font slot "${slot}" loads "${family}" from "${provider}" but also declares @face/italic faces \u2014 provider fonts can't be combined with local faces. The extra faces were ignored.`
|
|
1050
|
-
);
|
|
1051
|
-
}
|
|
1052
|
-
const config2 = createFontSlot({
|
|
1053
|
-
slot,
|
|
1054
|
-
family,
|
|
1055
|
-
kind,
|
|
1056
|
-
faces: [buildFace(provider, block.faceDefaults)]
|
|
1057
|
-
});
|
|
1058
|
-
applySlotOptions(config2, block.slotOpts);
|
|
1059
|
-
return config2;
|
|
1060
|
-
}
|
|
1061
|
-
const config = createFontSlot({ slot, family, kind, faces: buildFaces(provider, block) });
|
|
1062
|
-
applySlotOptions(config, block.slotOpts);
|
|
1063
|
-
warnDuplicateFaces(config, warnings);
|
|
1064
|
-
return config;
|
|
1065
|
-
}
|
|
1066
|
-
const manualMatch = matchable.match(/^(.+?)(?:\s*\{([\s\S]*)\})?$/);
|
|
1067
|
-
if (manualMatch) {
|
|
1068
|
-
const stackParts = manualMatch[1].trim().split(",").map((s) => s.trim());
|
|
1069
|
-
const family = stackParts[0].replace(/["']/g, "");
|
|
1070
|
-
if (!SAFE_FONT_FAMILY_RE.test(family)) {
|
|
1071
|
-
return createFontSlot({ slot, family: sanitizeFamily(family), kind: "manual" });
|
|
1072
|
-
}
|
|
1073
|
-
const fallback = stackParts.slice(1).map((s) => s.replace(/["']/g, "").trim());
|
|
1074
|
-
const block = processSlotBlock(manualMatch[2] || "", warnings);
|
|
1075
|
-
if (block.faceBlocks.length > 0 || block.italicSrcs.length > 0) {
|
|
1076
|
-
const config2 = createFontSlot({
|
|
1077
|
-
slot,
|
|
1078
|
-
family,
|
|
1079
|
-
kind: "local",
|
|
1080
|
-
fallback,
|
|
1081
|
-
faces: buildFaces(null, block)
|
|
1082
|
-
});
|
|
1083
|
-
applySlotOptions(config2, block.slotOpts);
|
|
1084
|
-
warnDuplicateFaces(config2, warnings);
|
|
1085
|
-
return config2;
|
|
1086
|
-
}
|
|
1087
|
-
const config = createFontSlot({
|
|
1088
|
-
slot,
|
|
1089
|
-
family,
|
|
1090
|
-
kind: "manual",
|
|
1091
|
-
fallback,
|
|
1092
|
-
faces: [buildFace("", block.faceDefaults)]
|
|
1093
|
-
});
|
|
1094
|
-
applySlotOptions(config, block.slotOpts);
|
|
1095
|
-
return config;
|
|
1096
|
-
}
|
|
1097
|
-
return createFontSlot({
|
|
1098
|
-
slot,
|
|
1099
|
-
family: sanitizeFamily(normalizedBody.replace(/["']/g, "")),
|
|
1100
|
-
kind: "manual"
|
|
1101
|
-
});
|
|
1318
|
+
function stripLeadingClassDot(name) {
|
|
1319
|
+
return name.startsWith(".") ? name.slice(1) : name;
|
|
1102
1320
|
}
|
|
1103
|
-
function
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
for (const entry of scanEntries(cleanedBody, { newlineTerminates: false })) {
|
|
1107
|
-
if (entry.removal || entry.fragment) continue;
|
|
1108
|
-
if (entry.unclosedBlock) break;
|
|
1109
|
-
if (!entry.key) continue;
|
|
1110
|
-
if (configs.length >= MAX_FONT_CONFIGS) {
|
|
1321
|
+
function isValidUtilityName(name, warnings) {
|
|
1322
|
+
if (IDENT_KEY_RE.test(name)) {
|
|
1323
|
+
if (/[A-Z]/.test(name)) {
|
|
1111
1324
|
warnings?.push(
|
|
1112
|
-
`[RI-
|
|
1325
|
+
`[RI-1038] @utility name "${name}" contains uppercase letters \u2014 the markup scanner only matches lowercase tokens, so class="${name}" will never generate it. It still works via @a/@apply and inline @source. Prefer a lowercase-hyphen name.`
|
|
1113
1326
|
);
|
|
1114
|
-
break;
|
|
1115
|
-
}
|
|
1116
|
-
if (entry.block !== void 0) {
|
|
1117
|
-
configs.push(parseFontBody(`${entry.value} { ${entry.block} }`, entry.key, warnings));
|
|
1118
|
-
} else if (entry.value) {
|
|
1119
|
-
configs.push(parseFontBody(entry.value, entry.key, warnings));
|
|
1120
1327
|
}
|
|
1328
|
+
return true;
|
|
1121
1329
|
}
|
|
1122
|
-
return configs;
|
|
1123
|
-
}
|
|
1124
|
-
function stripLeadingClassDot(name) {
|
|
1125
|
-
return name.startsWith(".") ? name.slice(1) : name;
|
|
1126
|
-
}
|
|
1127
|
-
function isValidUtilityName(name, warnings) {
|
|
1128
|
-
if (IDENT_KEY_RE.test(name)) return true;
|
|
1129
1330
|
warnings?.push(
|
|
1130
1331
|
`[RI-1035] Invalid @utility name "${name}" \u2014 names may only contain letters, numbers, hyphens, and underscores (plus an optional trailing "-*" for functional utilities). The utility was skipped.`
|
|
1131
1332
|
);
|
|
@@ -2996,6 +3197,17 @@ function parseCustomUtilityBody(body) {
|
|
|
2996
3197
|
flushStatement(body.length);
|
|
2997
3198
|
return root;
|
|
2998
3199
|
}
|
|
3200
|
+
var parsedBodyCache = /* @__PURE__ */ new Map();
|
|
3201
|
+
var PARSED_BODY_CACHE_MAX = 500;
|
|
3202
|
+
function getParsedBody(body) {
|
|
3203
|
+
let tree = parsedBodyCache.get(body);
|
|
3204
|
+
if (!tree) {
|
|
3205
|
+
if (parsedBodyCache.size >= PARSED_BODY_CACHE_MAX) parsedBodyCache.clear();
|
|
3206
|
+
tree = parseCustomUtilityBody(body);
|
|
3207
|
+
parsedBodyCache.set(body, tree);
|
|
3208
|
+
}
|
|
3209
|
+
return tree;
|
|
3210
|
+
}
|
|
2999
3211
|
var MAX_CUSTOM_APPLY_DEPTH = 5;
|
|
3000
3212
|
var APPLY_CLASS_SPLIT_RE = /\s+/;
|
|
3001
3213
|
function forEachApplyClass(body, visit) {
|
|
@@ -3039,7 +3251,7 @@ function resolveCustomUtility(utility, value, theme, resolve, visiting) {
|
|
|
3039
3251
|
const map = getCustomUtilityMap(theme);
|
|
3040
3252
|
const cu = value === null ? map.get(utility) : map.get(`${utility}-${value}`);
|
|
3041
3253
|
if (!cu || cu.functional) return null;
|
|
3042
|
-
const tree =
|
|
3254
|
+
const tree = getParsedBody(cu.body);
|
|
3043
3255
|
let expansion = null;
|
|
3044
3256
|
if (hasApplyLikeDirective(cu.body)) {
|
|
3045
3257
|
const visited = visiting ?? /* @__PURE__ */ new Set();
|
|
@@ -3063,7 +3275,7 @@ function resolveCustomUtility(utility, value, theme, resolve, visiting) {
|
|
|
3063
3275
|
return { declarations, nested };
|
|
3064
3276
|
}
|
|
3065
3277
|
function extractCustomUtilityRootInfo(body) {
|
|
3066
|
-
const tree =
|
|
3278
|
+
const tree = getParsedBody(body);
|
|
3067
3279
|
const seen = /* @__PURE__ */ new Set();
|
|
3068
3280
|
const properties = [];
|
|
3069
3281
|
for (const d of tree.declarations) {
|
|
@@ -3072,7 +3284,7 @@ function extractCustomUtilityRootInfo(body) {
|
|
|
3072
3284
|
properties.push(d.property);
|
|
3073
3285
|
}
|
|
3074
3286
|
}
|
|
3075
|
-
return { properties, applyClasses: tree.applyClasses };
|
|
3287
|
+
return { properties, applyClasses: [...tree.applyClasses] };
|
|
3076
3288
|
}
|
|
3077
3289
|
|
|
3078
3290
|
// src/utilities/helpers.ts
|
|
@@ -8424,6 +8636,48 @@ function createEmptyCompilationResult() {
|
|
|
8424
8636
|
warnings: []
|
|
8425
8637
|
};
|
|
8426
8638
|
}
|
|
8639
|
+
var classCompileMemo = /* @__PURE__ */ new WeakMap();
|
|
8640
|
+
function compileClassEntry(raw, theme, customVariantMap, breakpointWeights, variantMemo) {
|
|
8641
|
+
const scratch = createEmptyCompilationResult();
|
|
8642
|
+
const parsed = parseUtility(raw);
|
|
8643
|
+
const rule = compileUtility(
|
|
8644
|
+
parsed,
|
|
8645
|
+
theme,
|
|
8646
|
+
scratch,
|
|
8647
|
+
customVariantMap,
|
|
8648
|
+
/* @__PURE__ */ new Set(),
|
|
8649
|
+
breakpointWeights,
|
|
8650
|
+
variantMemo
|
|
8651
|
+
);
|
|
8652
|
+
const support = new Array(SUPPORT_BLOCKS.length).fill(false);
|
|
8653
|
+
if (rule) {
|
|
8654
|
+
for (let i = 0; i < SUPPORT_BLOCKS.length; i++) {
|
|
8655
|
+
const block = SUPPORT_BLOCKS[i];
|
|
8656
|
+
if (block.test(rule.css) || block.utilities?.includes(parsed.utility)) {
|
|
8657
|
+
support[i] = true;
|
|
8658
|
+
}
|
|
8659
|
+
}
|
|
8660
|
+
} else if (parsed.arbitrary && parsed.value) {
|
|
8661
|
+
const bracketContent = parsed.value.replace(/^\[|\]$/g, "");
|
|
8662
|
+
if (shouldWarnUnresolvedArbitrary(bracketContent)) {
|
|
8663
|
+
const truncated = parsed.raw.length > 100 ? `${parsed.raw.slice(0, 100)}...` : parsed.raw;
|
|
8664
|
+
scratch.warnings.push(
|
|
8665
|
+
`[RI-1002] Could not resolve arbitrary utility "${truncated}". Arbitrary utilities use \`[property:value]\` syntax \u2014 e.g. \`[padding:1rem]\` or \`[mask-type:luminance]\`. Check that the property name is a known CSS property and the value is well-formed (no stray spaces, quoted strings escaped). If you meant to set a CSS variable, use \`[--my-var:value]\`.`
|
|
8666
|
+
);
|
|
8667
|
+
}
|
|
8668
|
+
}
|
|
8669
|
+
return {
|
|
8670
|
+
rule,
|
|
8671
|
+
warnings: scratch.warnings,
|
|
8672
|
+
usedColorStops: scratch.usedColorStops,
|
|
8673
|
+
usedTextSizes: scratch.usedTextSizes,
|
|
8674
|
+
usedFonts: scratch.usedFonts,
|
|
8675
|
+
usedRounded: scratch.usedRounded,
|
|
8676
|
+
usedShadows: scratch.usedShadows,
|
|
8677
|
+
usedAnimations: scratch.usedAnimations,
|
|
8678
|
+
support
|
|
8679
|
+
};
|
|
8680
|
+
}
|
|
8427
8681
|
function shouldWarnUnresolvedArbitrary(bracketContent) {
|
|
8428
8682
|
if (/[=*]/.test(bracketContent)) return false;
|
|
8429
8683
|
if (/\s/.test(bracketContent)) return false;
|
|
@@ -8476,42 +8730,39 @@ function compileInternal(classNames, theme, variantMapCache) {
|
|
|
8476
8730
|
const variantMemo = /* @__PURE__ */ new Map();
|
|
8477
8731
|
const seen = /* @__PURE__ */ new Set();
|
|
8478
8732
|
const supportNeeded = new Array(SUPPORT_BLOCKS.length).fill(false);
|
|
8733
|
+
let classMemo = classCompileMemo.get(theme);
|
|
8734
|
+
if (!classMemo) {
|
|
8735
|
+
classMemo = /* @__PURE__ */ new Map();
|
|
8736
|
+
classCompileMemo.set(theme, classMemo);
|
|
8737
|
+
}
|
|
8479
8738
|
for (const raw of classNames) {
|
|
8480
8739
|
if (seen.has(raw)) continue;
|
|
8481
8740
|
seen.add(raw);
|
|
8482
|
-
|
|
8483
|
-
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
warnSeen
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
if (shouldWarnUnresolvedArbitrary(bracketContent)) {
|
|
8496
|
-
const truncated = parsed.raw.length > 100 ? `${parsed.raw.slice(0, 100)}...` : parsed.raw;
|
|
8497
|
-
pushWarningsDeduped(
|
|
8498
|
-
result.warnings,
|
|
8499
|
-
[
|
|
8500
|
-
`[RI-1002] Could not resolve arbitrary utility "${truncated}". Arbitrary utilities use \`[property:value]\` syntax \u2014 e.g. \`[padding:1rem]\` or \`[mask-type:luminance]\`. Check that the property name is a known CSS property and the value is well-formed (no stray spaces, quoted strings escaped). If you meant to set a CSS variable, use \`[--my-var:value]\`.`
|
|
8501
|
-
],
|
|
8502
|
-
warnSeen
|
|
8503
|
-
);
|
|
8504
|
-
}
|
|
8741
|
+
let entry = classMemo.get(raw);
|
|
8742
|
+
if (!entry) {
|
|
8743
|
+
entry = compileClassEntry(raw, theme, customVariantMap, breakpointWeights, variantMemo);
|
|
8744
|
+
classMemo.set(raw, entry);
|
|
8745
|
+
}
|
|
8746
|
+
if (entry.warnings.length > 0) {
|
|
8747
|
+
pushWarningsDeduped(result.warnings, entry.warnings, warnSeen);
|
|
8748
|
+
}
|
|
8749
|
+
for (const [hue, stops] of entry.usedColorStops) {
|
|
8750
|
+
let set = result.usedColorStops.get(hue);
|
|
8751
|
+
if (!set) {
|
|
8752
|
+
set = /* @__PURE__ */ new Set();
|
|
8753
|
+
result.usedColorStops.set(hue, set);
|
|
8505
8754
|
}
|
|
8506
|
-
|
|
8507
|
-
}
|
|
8508
|
-
result.
|
|
8755
|
+
for (const stop of stops) set.add(stop);
|
|
8756
|
+
}
|
|
8757
|
+
for (const v of entry.usedTextSizes) result.usedTextSizes.add(v);
|
|
8758
|
+
for (const v of entry.usedFonts) result.usedFonts.add(v);
|
|
8759
|
+
for (const v of entry.usedRounded) result.usedRounded.add(v);
|
|
8760
|
+
for (const v of entry.usedShadows) result.usedShadows.add(v);
|
|
8761
|
+
for (const v of entry.usedAnimations) result.usedAnimations.add(v);
|
|
8762
|
+
if (!entry.rule) continue;
|
|
8763
|
+
result.rules.push(entry.rule);
|
|
8509
8764
|
for (let i = 0; i < SUPPORT_BLOCKS.length; i++) {
|
|
8510
|
-
if (
|
|
8511
|
-
const block = SUPPORT_BLOCKS[i];
|
|
8512
|
-
if (block.test(compiled.css) || block.utilities?.includes(parsed.utility)) {
|
|
8513
|
-
supportNeeded[i] = true;
|
|
8514
|
-
}
|
|
8765
|
+
if (entry.support[i]) supportNeeded[i] = true;
|
|
8515
8766
|
}
|
|
8516
8767
|
}
|
|
8517
8768
|
result.rules.sort(
|
|
@@ -8961,7 +9212,8 @@ function readAssignedValue(source, start) {
|
|
|
8961
9212
|
return {
|
|
8962
9213
|
value: source.slice(i + 1, end2),
|
|
8963
9214
|
end: end2,
|
|
8964
|
-
valueStart: i + 1
|
|
9215
|
+
valueStart: i + 1,
|
|
9216
|
+
quoted: true
|
|
8965
9217
|
};
|
|
8966
9218
|
}
|
|
8967
9219
|
if (ch === "`") {
|
|
@@ -8969,7 +9221,8 @@ function readAssignedValue(source, start) {
|
|
|
8969
9221
|
return {
|
|
8970
9222
|
value: source.slice(i + 1, end2),
|
|
8971
9223
|
end: end2,
|
|
8972
|
-
valueStart: i + 1
|
|
9224
|
+
valueStart: i + 1,
|
|
9225
|
+
quoted: false
|
|
8973
9226
|
};
|
|
8974
9227
|
}
|
|
8975
9228
|
if (ch in BRACKET_PAIRS) {
|
|
@@ -8978,7 +9231,8 @@ function readAssignedValue(source, start) {
|
|
|
8978
9231
|
return {
|
|
8979
9232
|
value: source.slice(i + 1, end2),
|
|
8980
9233
|
end: end2,
|
|
8981
|
-
valueStart: i + 1
|
|
9234
|
+
valueStart: i + 1,
|
|
9235
|
+
quoted: false
|
|
8982
9236
|
};
|
|
8983
9237
|
}
|
|
8984
9238
|
let end = i;
|
|
@@ -8986,7 +9240,8 @@ function readAssignedValue(source, start) {
|
|
|
8986
9240
|
return {
|
|
8987
9241
|
value: source.slice(i, end),
|
|
8988
9242
|
end: end - 1,
|
|
8989
|
-
valueStart: i
|
|
9243
|
+
valueStart: i,
|
|
9244
|
+
quoted: false
|
|
8990
9245
|
};
|
|
8991
9246
|
}
|
|
8992
9247
|
function splitTopLevelArgs(source) {
|
|
@@ -9087,6 +9342,8 @@ var CandidateCollector = class {
|
|
|
9087
9342
|
origin = "plain";
|
|
9088
9343
|
helperName = null;
|
|
9089
9344
|
byCandidate = /* @__PURE__ */ new Map();
|
|
9345
|
+
/** value -> byCandidate keys, so delete() is O(occurrences of the value). */
|
|
9346
|
+
keysByValue = /* @__PURE__ */ new Map();
|
|
9090
9347
|
contexts = [];
|
|
9091
9348
|
setOrigin(origin) {
|
|
9092
9349
|
this.origin = origin;
|
|
@@ -9111,22 +9368,45 @@ var CandidateCollector = class {
|
|
|
9111
9368
|
const candidate = { value, start, end, origin: "plain" };
|
|
9112
9369
|
if (prefixStart >= 0) candidate.groupPrefix = { start: prefixStart, end: prefixEnd };
|
|
9113
9370
|
this.byCandidate.set(key, candidate);
|
|
9371
|
+
let keys = this.keysByValue.get(value);
|
|
9372
|
+
if (!keys) {
|
|
9373
|
+
keys = /* @__PURE__ */ new Set();
|
|
9374
|
+
this.keysByValue.set(value, keys);
|
|
9375
|
+
}
|
|
9376
|
+
keys.add(key);
|
|
9114
9377
|
}
|
|
9115
9378
|
delete(value) {
|
|
9116
|
-
|
|
9117
|
-
|
|
9118
|
-
|
|
9379
|
+
const keys = this.keysByValue.get(value);
|
|
9380
|
+
if (!keys) return;
|
|
9381
|
+
for (const key of keys) this.byCandidate.delete(key);
|
|
9382
|
+
this.keysByValue.delete(value);
|
|
9119
9383
|
}
|
|
9120
9384
|
finish() {
|
|
9121
9385
|
const candidates = [...this.byCandidate.values()].sort(
|
|
9122
9386
|
(a, b) => a.start - b.start || a.end - b.end
|
|
9123
9387
|
);
|
|
9124
9388
|
if (this.contexts.length > 0) {
|
|
9389
|
+
const byStart = [...this.contexts].sort((a, b) => a.start - b.start);
|
|
9390
|
+
const live = [];
|
|
9391
|
+
let next = 0;
|
|
9125
9392
|
for (const candidate of candidates) {
|
|
9393
|
+
while (next < byStart.length && byStart[next].start <= candidate.start) {
|
|
9394
|
+
live.push(byStart[next]);
|
|
9395
|
+
next++;
|
|
9396
|
+
}
|
|
9126
9397
|
let best = null;
|
|
9127
|
-
for (
|
|
9128
|
-
|
|
9129
|
-
|
|
9398
|
+
for (let i = 0; i < live.length; i++) {
|
|
9399
|
+
const context = live[i];
|
|
9400
|
+
if (context.end < candidate.start) {
|
|
9401
|
+
live[i] = live[live.length - 1];
|
|
9402
|
+
live.pop();
|
|
9403
|
+
i--;
|
|
9404
|
+
continue;
|
|
9405
|
+
}
|
|
9406
|
+
if (candidate.end <= context.end) {
|
|
9407
|
+
const width = context.end - context.start;
|
|
9408
|
+
const bestWidth = best ? best.end - best.start : -1;
|
|
9409
|
+
if (!best || width < bestWidth || width === bestWidth && context.id < best.id) {
|
|
9130
9410
|
best = context;
|
|
9131
9411
|
}
|
|
9132
9412
|
}
|
|
@@ -9156,7 +9436,7 @@ var ARBITRARY_PROPERTY = /\[[a-z-][^\]]*:[^\]]+\](?:\/(?:[\w.]+|\[[^\]]*\]|\([^)
|
|
|
9156
9436
|
var CLASS_RE_SOURCE = `${BOUNDARY}(${NEGATIVE}${VARIANT_PREFIX}(?:${UTILITY_VALUE}|${VARIANT_GROUP}|${ARBITRARY_PROPERTY})${IMPORTANT_SUFFIX})`;
|
|
9157
9437
|
var CLASS_RE = new RegExp(CLASS_RE_SOURCE, "g");
|
|
9158
9438
|
var VARIANT_STRIP_RE = new RegExp(`^(?:${VARIANT_SEGMENT})+`);
|
|
9159
|
-
var MAX_LINE_LENGTH =
|
|
9439
|
+
var MAX_LINE_LENGTH = 1e4;
|
|
9160
9440
|
var CLASS_HELPERS = [
|
|
9161
9441
|
"clsx",
|
|
9162
9442
|
"cn",
|
|
@@ -9359,12 +9639,11 @@ function collectAssignedValues(sink, source, regex, visitor = scanClassTokens, b
|
|
|
9359
9639
|
const value = raw.trim();
|
|
9360
9640
|
if (!value) continue;
|
|
9361
9641
|
sink.markContext?.(base + parsed.valueStart, base + parsed.valueStart + raw.length);
|
|
9362
|
-
|
|
9363
|
-
|
|
9364
|
-
value,
|
|
9365
|
-
|
|
9366
|
-
|
|
9367
|
-
);
|
|
9642
|
+
const valueOffset = base + parsed.valueStart + (raw.length - raw.trimStart().length);
|
|
9643
|
+
if (parsed.quoted && visitor !== scanClassTokens) {
|
|
9644
|
+
scanClassTokens(sink, value, valueOffset, warnings);
|
|
9645
|
+
}
|
|
9646
|
+
visitor(sink, value, valueOffset, warnings);
|
|
9368
9647
|
regex.lastIndex = Math.max(regex.lastIndex, parsed.end + 1);
|
|
9369
9648
|
}
|
|
9370
9649
|
}
|
|
@@ -9538,10 +9817,8 @@ var NOOP_VISITOR = () => void 0;
|
|
|
9538
9817
|
function extractHTML(sink, context, warnings) {
|
|
9539
9818
|
sink.setOrigin?.("plain");
|
|
9540
9819
|
scanClassTokens(sink, context.content, 0, warnings);
|
|
9541
|
-
|
|
9542
|
-
|
|
9543
|
-
collectAssignedValues(sink, context.content, /\bclass\s*=/g, NOOP_VISITOR, 0, warnings);
|
|
9544
|
-
}
|
|
9820
|
+
sink.setOrigin?.("attribute");
|
|
9821
|
+
collectAssignedValues(sink, context.content, /\bclass\s*=/g, NOOP_VISITOR, 0, warnings);
|
|
9545
9822
|
}
|
|
9546
9823
|
function extractJSXTSX(sink, context, warnings) {
|
|
9547
9824
|
const content = context.content;
|
|
@@ -9585,16 +9862,7 @@ function extractVue(sink, context, warnings) {
|
|
|
9585
9862
|
0,
|
|
9586
9863
|
warnings
|
|
9587
9864
|
);
|
|
9588
|
-
|
|
9589
|
-
collectAssignedValues(
|
|
9590
|
-
sink,
|
|
9591
|
-
context.content,
|
|
9592
|
-
/(?<![:\w-])class\s*=/g,
|
|
9593
|
-
NOOP_VISITOR,
|
|
9594
|
-
0,
|
|
9595
|
-
warnings
|
|
9596
|
-
);
|
|
9597
|
-
}
|
|
9865
|
+
collectAssignedValues(sink, context.content, /(?<![:\w-])class\s*=/g, NOOP_VISITOR, 0, warnings);
|
|
9598
9866
|
}
|
|
9599
9867
|
function extractSvelte(sink, context, warnings) {
|
|
9600
9868
|
sink.setOrigin?.("plain");
|
|
@@ -9628,7 +9896,26 @@ var EXTRACTORS = [
|
|
|
9628
9896
|
extract: extractJSXTSX
|
|
9629
9897
|
}
|
|
9630
9898
|
];
|
|
9899
|
+
function warnOverLongLines(input, warnings) {
|
|
9900
|
+
const content = input.content;
|
|
9901
|
+
if (!warnings || content.length <= MAX_LINE_LENGTH) return;
|
|
9902
|
+
if (input.path?.includes("node_modules")) return;
|
|
9903
|
+
let count = 0;
|
|
9904
|
+
let start = 0;
|
|
9905
|
+
for (; ; ) {
|
|
9906
|
+
const idx = content.indexOf("\n", start);
|
|
9907
|
+
const end = idx === -1 ? content.length : idx;
|
|
9908
|
+
if (end - start > MAX_LINE_LENGTH) count++;
|
|
9909
|
+
if (idx === -1) break;
|
|
9910
|
+
start = idx + 1;
|
|
9911
|
+
}
|
|
9912
|
+
if (count === 0) return;
|
|
9913
|
+
warnings.push(
|
|
9914
|
+
`[RI-1411] ${input.path ?? "<source>"}: ${count} line(s) longer than ${MAX_LINE_LENGTH} characters were skipped by the class scanner (minified-input guard). Quoted class attributes on those lines are still read; other class references there are not \u2014 split the long lines.`
|
|
9915
|
+
);
|
|
9916
|
+
}
|
|
9631
9917
|
function extractInto(sink, input, warnings) {
|
|
9918
|
+
warnOverLongLines(input, warnings);
|
|
9632
9919
|
let handled = false;
|
|
9633
9920
|
for (const extractor of EXTRACTORS) {
|
|
9634
9921
|
if (extractor.test(input)) {
|
|
@@ -9716,15 +10003,15 @@ function analyzeProjectCSS(css) {
|
|
|
9716
10003
|
}
|
|
9717
10004
|
|
|
9718
10005
|
export {
|
|
10006
|
+
isRIDebug,
|
|
10007
|
+
withTimeout,
|
|
10008
|
+
isAtRuleBoundary,
|
|
10009
|
+
codepointCompare,
|
|
9719
10010
|
DIRECTIVE_TYPE_NAMES,
|
|
9720
10011
|
findClosingBrace,
|
|
9721
10012
|
APPLY_ALIASES,
|
|
9722
10013
|
hasApplyLikeDirective,
|
|
9723
10014
|
APPLY_LIKE_MATCH_RE,
|
|
9724
|
-
isRIDebug,
|
|
9725
|
-
withTimeout,
|
|
9726
|
-
isAtRuleBoundary,
|
|
9727
|
-
codepointCompare,
|
|
9728
10015
|
DIRECTIVE_NAMES_SET,
|
|
9729
10016
|
directiveAtRulePattern,
|
|
9730
10017
|
isAtRuleNameChar,
|