@hyperframes/lint 0.8.29 → 0.8.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.js +298 -23
- package/dist/browser.js.map +1 -1
- package/dist/index.js +298 -23
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -188,57 +188,311 @@ function stripStringLiterals(source) {
|
|
|
188
188
|
(literal) => literal[0] + " ".repeat(Math.max(0, literal.length - 1))
|
|
189
189
|
);
|
|
190
190
|
}
|
|
191
|
-
function
|
|
191
|
+
function scanJsComments(source) {
|
|
192
192
|
let out = "";
|
|
193
193
|
let i = 0;
|
|
194
194
|
let quote = null;
|
|
195
195
|
let escaped = false;
|
|
196
|
+
let inRegex = false;
|
|
197
|
+
let inRegexClass = false;
|
|
198
|
+
let regexMisread = false;
|
|
199
|
+
const ctx = new CodeContext();
|
|
200
|
+
const emitCode = (ch) => {
|
|
201
|
+
out += ch;
|
|
202
|
+
ctx.push(ch);
|
|
203
|
+
};
|
|
204
|
+
const emitOpaque = (ch) => {
|
|
205
|
+
out += ch;
|
|
206
|
+
ctx.push(" ");
|
|
207
|
+
};
|
|
196
208
|
while (i < source.length) {
|
|
197
209
|
const ch = source[i] ?? "";
|
|
198
210
|
const next = source[i + 1] ?? "";
|
|
199
|
-
if (
|
|
211
|
+
if (inRegex) {
|
|
200
212
|
out += ch;
|
|
201
213
|
if (escaped) {
|
|
202
214
|
escaped = false;
|
|
215
|
+
if (ch === "\n" || ch === "\r") {
|
|
216
|
+
inRegex = false;
|
|
217
|
+
inRegexClass = false;
|
|
218
|
+
regexMisread = true;
|
|
219
|
+
}
|
|
203
220
|
} else if (ch === "\\") {
|
|
204
221
|
escaped = true;
|
|
222
|
+
} else if (ch === "[") {
|
|
223
|
+
inRegexClass = true;
|
|
224
|
+
} else if (ch === "]") {
|
|
225
|
+
inRegexClass = false;
|
|
226
|
+
} else if (ch === "/" && !inRegexClass) {
|
|
227
|
+
inRegex = false;
|
|
228
|
+
ctx.push(ch);
|
|
229
|
+
} else if (ch === "\n" || ch === "\r") {
|
|
230
|
+
inRegex = false;
|
|
231
|
+
inRegexClass = false;
|
|
232
|
+
regexMisread = true;
|
|
233
|
+
}
|
|
234
|
+
i += 1;
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
if (quote) {
|
|
238
|
+
if (escaped) {
|
|
239
|
+
escaped = false;
|
|
240
|
+
emitOpaque(ch);
|
|
241
|
+
} else if (ch === "\\") {
|
|
242
|
+
escaped = true;
|
|
243
|
+
emitOpaque(ch);
|
|
205
244
|
} else if (ch === quote) {
|
|
206
245
|
quote = null;
|
|
246
|
+
emitCode(ch);
|
|
247
|
+
} else {
|
|
248
|
+
emitOpaque(ch);
|
|
207
249
|
}
|
|
208
250
|
i += 1;
|
|
209
251
|
continue;
|
|
210
252
|
}
|
|
211
253
|
if (ch === "'" || ch === '"' || ch === "`") {
|
|
212
254
|
quote = ch;
|
|
213
|
-
|
|
255
|
+
emitCode(ch);
|
|
214
256
|
i += 1;
|
|
215
257
|
continue;
|
|
216
258
|
}
|
|
217
259
|
if (ch === "/" && next === "/") {
|
|
218
260
|
out += " ";
|
|
261
|
+
ctx.push(" ");
|
|
262
|
+
ctx.push(" ");
|
|
219
263
|
i += 2;
|
|
220
264
|
while (i < source.length && source[i] !== "\n" && source[i] !== "\r") {
|
|
221
265
|
out += " ";
|
|
266
|
+
ctx.push(" ");
|
|
222
267
|
i += 1;
|
|
223
268
|
}
|
|
224
269
|
continue;
|
|
225
270
|
}
|
|
226
271
|
if (ch === "/" && next === "*") {
|
|
227
272
|
out += " ";
|
|
273
|
+
ctx.push(" ");
|
|
274
|
+
ctx.push(" ");
|
|
228
275
|
i += 2;
|
|
229
276
|
while (i < source.length) {
|
|
230
277
|
const blockCh = source[i] ?? "";
|
|
231
278
|
const blockNext = source[i + 1] ?? "";
|
|
232
279
|
if (blockCh === "*" && blockNext === "/") {
|
|
233
280
|
out += " ";
|
|
281
|
+
ctx.push(" ");
|
|
282
|
+
ctx.push(" ");
|
|
234
283
|
i += 2;
|
|
235
284
|
break;
|
|
236
285
|
}
|
|
237
|
-
|
|
286
|
+
const kept = blockCh === "\n" || blockCh === "\r" ? blockCh : " ";
|
|
287
|
+
out += kept;
|
|
288
|
+
ctx.push(kept);
|
|
238
289
|
i += 1;
|
|
239
290
|
}
|
|
240
291
|
continue;
|
|
241
292
|
}
|
|
293
|
+
if (ch === "/" && ctx.startsRegexLiteral()) {
|
|
294
|
+
inRegex = true;
|
|
295
|
+
emitCode(ch);
|
|
296
|
+
i += 1;
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
emitCode(ch);
|
|
300
|
+
i += 1;
|
|
301
|
+
}
|
|
302
|
+
return { out, balanced: quote === null && !inRegex && !regexMisread };
|
|
303
|
+
}
|
|
304
|
+
function stripJsComments(source) {
|
|
305
|
+
return scanJsComments(source).out;
|
|
306
|
+
}
|
|
307
|
+
function stripJsCode(source) {
|
|
308
|
+
const { out, balanced } = scanJsComments(source);
|
|
309
|
+
return balanced ? stripJsStringLiterals(out) : source;
|
|
310
|
+
}
|
|
311
|
+
var REGEX_ALLOWED_BEFORE = new Set("=(,:[!&|?{};+-*%^~<>");
|
|
312
|
+
var REGEX_ALLOWED_KEYWORDS = /* @__PURE__ */ new Set([
|
|
313
|
+
"return",
|
|
314
|
+
"typeof",
|
|
315
|
+
"instanceof",
|
|
316
|
+
"in",
|
|
317
|
+
"of",
|
|
318
|
+
"new",
|
|
319
|
+
"delete",
|
|
320
|
+
"void",
|
|
321
|
+
"case",
|
|
322
|
+
"do",
|
|
323
|
+
"else",
|
|
324
|
+
"yield",
|
|
325
|
+
"await"
|
|
326
|
+
]);
|
|
327
|
+
var WORD_CHAR = /[A-Za-z0-9_$]/;
|
|
328
|
+
var CodeContext = class {
|
|
329
|
+
last = "";
|
|
330
|
+
prev = "";
|
|
331
|
+
word = "";
|
|
332
|
+
wordEnded = false;
|
|
333
|
+
wordAfterDot = false;
|
|
334
|
+
push(ch) {
|
|
335
|
+
if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
|
|
336
|
+
this.wordEnded = true;
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (WORD_CHAR.test(ch)) {
|
|
340
|
+
if (this.wordEnded || this.word === "") this.wordAfterDot = this.last === ".";
|
|
341
|
+
this.word = this.wordEnded ? ch : this.word + ch;
|
|
342
|
+
} else {
|
|
343
|
+
this.word = "";
|
|
344
|
+
this.wordAfterDot = false;
|
|
345
|
+
}
|
|
346
|
+
this.wordEnded = false;
|
|
347
|
+
this.prev = this.last;
|
|
348
|
+
this.last = ch;
|
|
349
|
+
}
|
|
350
|
+
startsRegexLiteral() {
|
|
351
|
+
if (this.last === "") return true;
|
|
352
|
+
if (WORD_CHAR.test(this.last))
|
|
353
|
+
return !this.wordAfterDot && REGEX_ALLOWED_KEYWORDS.has(this.word);
|
|
354
|
+
if ((this.last === "+" || this.last === "-") && this.prev === this.last) return false;
|
|
355
|
+
return REGEX_ALLOWED_BEFORE.has(this.last);
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
function stripJsStringLiterals(source) {
|
|
359
|
+
let out = "";
|
|
360
|
+
let i = 0;
|
|
361
|
+
const templateBraces = [];
|
|
362
|
+
const ctx = new CodeContext();
|
|
363
|
+
let quote = null;
|
|
364
|
+
let escaped = false;
|
|
365
|
+
let inRegex = false;
|
|
366
|
+
let inRegexClass = false;
|
|
367
|
+
let regexMisread = false;
|
|
368
|
+
const blank = (ch) => ch === "\n" || ch === "\r" ? ch : " ";
|
|
369
|
+
const emit = (text) => {
|
|
370
|
+
out += text;
|
|
371
|
+
for (const ch of text) ctx.push(ch);
|
|
372
|
+
};
|
|
373
|
+
while (i < source.length) {
|
|
374
|
+
const ch = source[i] ?? "";
|
|
375
|
+
const next = source[i + 1] ?? "";
|
|
376
|
+
if (inRegex) {
|
|
377
|
+
if (escaped) {
|
|
378
|
+
escaped = false;
|
|
379
|
+
if (ch === "\n" || ch === "\r") {
|
|
380
|
+
inRegex = false;
|
|
381
|
+
inRegexClass = false;
|
|
382
|
+
regexMisread = true;
|
|
383
|
+
}
|
|
384
|
+
emit(blank(ch));
|
|
385
|
+
} else if (ch === "\\") {
|
|
386
|
+
escaped = true;
|
|
387
|
+
emit(" ");
|
|
388
|
+
} else if (ch === "[") {
|
|
389
|
+
inRegexClass = true;
|
|
390
|
+
emit(" ");
|
|
391
|
+
} else if (ch === "]") {
|
|
392
|
+
inRegexClass = false;
|
|
393
|
+
emit(" ");
|
|
394
|
+
} else if (ch === "/" && !inRegexClass) {
|
|
395
|
+
inRegex = false;
|
|
396
|
+
emit(ch);
|
|
397
|
+
} else if (ch === "\n" || ch === "\r") {
|
|
398
|
+
inRegex = false;
|
|
399
|
+
inRegexClass = false;
|
|
400
|
+
escaped = false;
|
|
401
|
+
regexMisread = true;
|
|
402
|
+
emit(ch);
|
|
403
|
+
} else {
|
|
404
|
+
emit(" ");
|
|
405
|
+
}
|
|
406
|
+
i += 1;
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
if (quote) {
|
|
410
|
+
if (escaped) {
|
|
411
|
+
escaped = false;
|
|
412
|
+
emit(blank(ch));
|
|
413
|
+
} else if (ch === "\\") {
|
|
414
|
+
escaped = true;
|
|
415
|
+
emit(" ");
|
|
416
|
+
} else if (ch === quote) {
|
|
417
|
+
quote = null;
|
|
418
|
+
emit(ch);
|
|
419
|
+
} else if (ch === "`" || quote !== "`" || ch !== "$" || next !== "{") {
|
|
420
|
+
emit(blank(ch));
|
|
421
|
+
} else {
|
|
422
|
+
templateBraces.push(0);
|
|
423
|
+
quote = null;
|
|
424
|
+
emit("${");
|
|
425
|
+
i += 2;
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
i += 1;
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
if (ch === "'" || ch === '"' || ch === "`") {
|
|
432
|
+
quote = ch;
|
|
433
|
+
emit(ch);
|
|
434
|
+
i += 1;
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
if (ch === "/" && next !== "/" && next !== "*" && ctx.startsRegexLiteral()) {
|
|
438
|
+
inRegex = true;
|
|
439
|
+
emit(ch);
|
|
440
|
+
i += 1;
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
if (templateBraces.length > 0) {
|
|
444
|
+
const depth = templateBraces[templateBraces.length - 1] ?? 0;
|
|
445
|
+
if (ch === "{") templateBraces[templateBraces.length - 1] = depth + 1;
|
|
446
|
+
else if (ch === "}") {
|
|
447
|
+
if (depth === 0) {
|
|
448
|
+
templateBraces.pop();
|
|
449
|
+
quote = "`";
|
|
450
|
+
emit(ch);
|
|
451
|
+
i += 1;
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
templateBraces[templateBraces.length - 1] = depth - 1;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
emit(ch);
|
|
458
|
+
i += 1;
|
|
459
|
+
}
|
|
460
|
+
if (quote !== null || templateBraces.length > 0 || inRegex || regexMisread) return source;
|
|
461
|
+
return out;
|
|
462
|
+
}
|
|
463
|
+
function stripCssComments(source) {
|
|
464
|
+
let out = "";
|
|
465
|
+
let i = 0;
|
|
466
|
+
let quote = null;
|
|
467
|
+
while (i < source.length) {
|
|
468
|
+
const ch = source[i] ?? "";
|
|
469
|
+
if (quote) {
|
|
470
|
+
out += ch;
|
|
471
|
+
if (ch === "\\") {
|
|
472
|
+
out += source[i + 1] ?? "";
|
|
473
|
+
i += 2;
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
if (ch === quote) quote = null;
|
|
477
|
+
i += 1;
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
if (ch === '"' || ch === "'") {
|
|
481
|
+
quote = ch;
|
|
482
|
+
out += ch;
|
|
483
|
+
i += 1;
|
|
484
|
+
continue;
|
|
485
|
+
}
|
|
486
|
+
if (ch === "/" && source[i + 1] === "*") {
|
|
487
|
+
const end = source.indexOf("*/", i + 2);
|
|
488
|
+
const stop = end === -1 ? source.length : end + 2;
|
|
489
|
+
for (let j = i; j < stop; j += 1) {
|
|
490
|
+
const c = source[j] ?? "";
|
|
491
|
+
out += c === "\n" || c === "\r" ? c : " ";
|
|
492
|
+
}
|
|
493
|
+
i = stop;
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
242
496
|
out += ch;
|
|
243
497
|
i += 1;
|
|
244
498
|
}
|
|
@@ -806,6 +1060,12 @@ var VIDEO_SRC_EXT = /* @__PURE__ */ new Set([
|
|
|
806
1060
|
"mpg",
|
|
807
1061
|
"mpeg"
|
|
808
1062
|
]);
|
|
1063
|
+
var AUDIO_SRC_EXT = /* @__PURE__ */ new Set(["mp3", "wav", "aac", "flac", "opus", "aiff", "wma"]);
|
|
1064
|
+
var SRC_KIND_NOUN = {
|
|
1065
|
+
image: "an image",
|
|
1066
|
+
video: "a video",
|
|
1067
|
+
audio: "an audio file"
|
|
1068
|
+
};
|
|
809
1069
|
function srcKind(src) {
|
|
810
1070
|
const stripped = src.trim();
|
|
811
1071
|
if (!stripped) return null;
|
|
@@ -815,6 +1075,7 @@ function srcKind(src) {
|
|
|
815
1075
|
if (!mime) return null;
|
|
816
1076
|
if (mime.startsWith("image/")) return "image";
|
|
817
1077
|
if (mime.startsWith("video/")) return "video";
|
|
1078
|
+
if (mime.startsWith("audio/")) return "audio";
|
|
818
1079
|
return null;
|
|
819
1080
|
}
|
|
820
1081
|
if (lower.startsWith("blob:")) return null;
|
|
@@ -834,6 +1095,7 @@ function srcKind(src) {
|
|
|
834
1095
|
const ext = base.slice(dot + 1).toLowerCase();
|
|
835
1096
|
if (IMAGE_SRC_EXT.has(ext)) return "image";
|
|
836
1097
|
if (VIDEO_SRC_EXT.has(ext)) return "video";
|
|
1098
|
+
if (AUDIO_SRC_EXT.has(ext)) return "audio";
|
|
837
1099
|
return null;
|
|
838
1100
|
}
|
|
839
1101
|
function findMediaSrcKindMismatchFindings(ctx) {
|
|
@@ -844,16 +1106,15 @@ function findMediaSrcKindMismatchFindings(ctx) {
|
|
|
844
1106
|
if (!src) continue;
|
|
845
1107
|
const kind = srcKind(src);
|
|
846
1108
|
if (kind === null) continue;
|
|
847
|
-
if (tag.name === "video" && kind !== "image") continue;
|
|
848
|
-
if (tag.name === "img" && kind !== "video") continue;
|
|
849
|
-
const elementId = readAttr(tag.raw, "id") || void 0;
|
|
850
1109
|
const expected = tag.name === "video" ? "video" : "image";
|
|
1110
|
+
if (kind === expected) continue;
|
|
1111
|
+
const elementId = readAttr(tag.raw, "id") || void 0;
|
|
851
1112
|
findings.push({
|
|
852
1113
|
code: "media_src_kind_mismatch",
|
|
853
1114
|
severity: "error",
|
|
854
|
-
message: `<${tag.name}${elementId ? ` id="${elementId}"` : ""}> src is
|
|
1115
|
+
message: `<${tag.name}${elementId ? ` id="${elementId}"` : ""}> src is ${SRC_KIND_NOUN[kind]}, not ${SRC_KIND_NOUN[expected]}. The producer fail-closes when the tag and file kind disagree.`,
|
|
855
1116
|
elementId,
|
|
856
|
-
fixHint: tag.name === "video" ? "Use <img> for a still, or point <video> at a video URL (mp4/webm/mov/\u2026)." : "Use <video> for a video URL, or point <img> at a still (png/jpg/webp/\u2026).",
|
|
1117
|
+
fixHint: tag.name === "video" ? "Use <img> for a still, <audio> for sound, or point <video> at a video URL (mp4/webm/mov/\u2026)." : "Use <video> for a video URL, <audio> for sound, or point <img> at a still (png/jpg/webp/\u2026).",
|
|
857
1118
|
snippet: truncateSnippet(tag.raw)
|
|
858
1119
|
});
|
|
859
1120
|
}
|
|
@@ -1638,6 +1899,14 @@ function isHiddenGsapState(values) {
|
|
|
1638
1899
|
const display = stringValue(values.display)?.toLowerCase();
|
|
1639
1900
|
return zeroValue(values.opacity) || zeroValue(values.autoAlpha) || visibility === "hidden" || display === "none";
|
|
1640
1901
|
}
|
|
1902
|
+
function hiddenSetTargetSelectors(target, aliases) {
|
|
1903
|
+
const parts = target.startsWith("[") && target.endsWith("]") ? target.slice(1, -1).split(",") : [target];
|
|
1904
|
+
return parts.flatMap((part) => {
|
|
1905
|
+
const trimmed = part.trim();
|
|
1906
|
+
const resolved = /^(["'`])([^"'`]+)\1$/.exec(trimmed)?.[2] ?? aliases.get(trimmed);
|
|
1907
|
+
return resolved === void 0 ? [] : resolved.split(",");
|
|
1908
|
+
}).map((selector) => selector.trim()).filter((selector) => selector.length > 0);
|
|
1909
|
+
}
|
|
1641
1910
|
function extractStandaloneHiddenSelectors(script) {
|
|
1642
1911
|
const selectors = /* @__PURE__ */ new Set();
|
|
1643
1912
|
const source = stripJsComments(script);
|
|
@@ -1648,16 +1917,15 @@ function extractStandaloneHiddenSelectors(script) {
|
|
|
1648
1917
|
)) {
|
|
1649
1918
|
aliases.set(match2[1] ?? "", match2[3] ?? "");
|
|
1650
1919
|
}
|
|
1651
|
-
const pattern = /gsap\.set\s*\(\s*([^,]+?)\s*,\s*\{([\s\S]*?)\}\s*\)/g;
|
|
1920
|
+
const pattern = /gsap\.set\s*\(\s*(\[[^[\]]*\]|"[^"]*"|'[^']*'|`[^`]*`|[^,()[\]]+?)\s*,\s*\{([\s\S]*?)\}\s*\)/g;
|
|
1652
1921
|
let match;
|
|
1653
1922
|
while ((match = pattern.exec(source)) !== null) {
|
|
1654
1923
|
if (indexInsideNonIifeRange(match.index, source, functionRanges)) continue;
|
|
1655
|
-
const
|
|
1656
|
-
|
|
1657
|
-
if (!selector) continue;
|
|
1924
|
+
const targets = hiddenSetTargetSelectors((match[1] ?? "").trim(), aliases);
|
|
1925
|
+
if (targets.length === 0) continue;
|
|
1658
1926
|
const body = match[2] ?? "";
|
|
1659
1927
|
if (/(?:opacity|autoAlpha)\s*:\s*0(?:\.0+)?\s*(?:,|$)/.test(body)) {
|
|
1660
|
-
selectors.add(selector);
|
|
1928
|
+
for (const selector of targets) selectors.add(selector);
|
|
1661
1929
|
}
|
|
1662
1930
|
}
|
|
1663
1931
|
return selectors;
|
|
@@ -2237,6 +2505,9 @@ var gsapRules = [
|
|
|
2237
2505
|
// fallow-ignore-next-line complexity
|
|
2238
2506
|
async ({ source, tags, scripts, styles, rootCompositionId }) => {
|
|
2239
2507
|
const findings = [];
|
|
2508
|
+
const authoredHiddenSelectors = new Set(
|
|
2509
|
+
scripts.flatMap((script) => [...extractStandaloneHiddenSelectors(script.content)])
|
|
2510
|
+
);
|
|
2240
2511
|
const clipIds = /* @__PURE__ */ new Map();
|
|
2241
2512
|
const clipClasses = /* @__PURE__ */ new Map();
|
|
2242
2513
|
for (const tag of tags) {
|
|
@@ -2329,9 +2600,10 @@ ${right.raw}`)
|
|
|
2329
2600
|
);
|
|
2330
2601
|
}).sort((a, b) => a.position - b.position);
|
|
2331
2602
|
const startsHiddenAtZero = visibilityWindows.some(
|
|
2332
|
-
(win) => win.position <= SCENE_BOUNDARY_EPSILON_SECONDS && isHiddenGsapState(win.propertyValues)
|
|
2603
|
+
(win) => win.position <= SCENE_BOUNDARY_EPSILON_SECONDS && (isHiddenGsapState(win.propertyValues) || win.fromPropertyValues !== void 0 && isHiddenGsapState(win.fromPropertyValues))
|
|
2333
2604
|
);
|
|
2334
2605
|
if (startsHiddenAtZero) continue;
|
|
2606
|
+
if (selectors.some((selector2) => authoredHiddenSelectors.has(selector2))) continue;
|
|
2335
2607
|
const firstVisible = visibilityWindows.find((win) => makesOverlayVisible(win));
|
|
2336
2608
|
if (!firstVisible) continue;
|
|
2337
2609
|
const selector = selectors.find(
|
|
@@ -3799,8 +4071,8 @@ var compositionRules = [
|
|
|
3799
4071
|
});
|
|
3800
4072
|
}
|
|
3801
4073
|
};
|
|
3802
|
-
for (const style of styles) scan(style.content);
|
|
3803
|
-
for (const script of scripts) scan(script.content);
|
|
4074
|
+
for (const style of styles) scan(stripCssComments(style.content));
|
|
4075
|
+
for (const script of scripts) scan(stripJsComments(script.content));
|
|
3804
4076
|
return findings;
|
|
3805
4077
|
},
|
|
3806
4078
|
// template_literal_selector
|
|
@@ -3808,14 +4080,17 @@ var compositionRules = [
|
|
|
3808
4080
|
const findings = [];
|
|
3809
4081
|
for (const script of scripts) {
|
|
3810
4082
|
const templateLiteralSelectorPattern = /(?:querySelector|querySelectorAll)\s*\(\s*`[^`]*\$\{[^}]+\}[^`]*`\s*\)/g;
|
|
4083
|
+
const scanned = stripJsCode(script.content);
|
|
3811
4084
|
let tlMatch;
|
|
3812
|
-
while ((tlMatch = templateLiteralSelectorPattern.exec(
|
|
4085
|
+
while ((tlMatch = templateLiteralSelectorPattern.exec(scanned)) !== null) {
|
|
3813
4086
|
findings.push({
|
|
3814
4087
|
code: "template_literal_selector",
|
|
3815
4088
|
severity: "error",
|
|
3816
4089
|
message: "querySelector uses a template literal variable (e.g. `${compId}`). The HTML bundler's CSS parser crashes on these. Use a hardcoded string instead.",
|
|
3817
4090
|
fixHint: "Replace the template literal variable with a hardcoded string. The bundler's CSS parser cannot handle interpolated variables in script content.",
|
|
3818
|
-
snippet: truncateSnippet(
|
|
4091
|
+
snippet: truncateSnippet(
|
|
4092
|
+
script.content.slice(tlMatch.index, tlMatch.index + tlMatch[0].length)
|
|
4093
|
+
)
|
|
3819
4094
|
});
|
|
3820
4095
|
}
|
|
3821
4096
|
}
|
|
@@ -3917,7 +4192,7 @@ var compositionRules = [
|
|
|
3917
4192
|
if (isRegistrySourceFile(options.filePath) || isRegistryInstalledFile(rawSource)) return [];
|
|
3918
4193
|
const findings = [];
|
|
3919
4194
|
for (const script of scripts) {
|
|
3920
|
-
const stripped =
|
|
4195
|
+
const stripped = stripJsCode(script.content);
|
|
3921
4196
|
if (/requestAnimationFrame\s*\(/.test(stripped)) {
|
|
3922
4197
|
findings.push({
|
|
3923
4198
|
code: "requestanimationframe_in_composition",
|
|
@@ -4569,7 +4844,7 @@ var GENERIC_FAMILIES = /* @__PURE__ */ new Set([
|
|
|
4569
4844
|
"unset",
|
|
4570
4845
|
"revert"
|
|
4571
4846
|
]);
|
|
4572
|
-
function
|
|
4847
|
+
function stripCssComments2(css) {
|
|
4573
4848
|
return css.replace(/\/\*[\s\S]*?\*\//g, " ");
|
|
4574
4849
|
}
|
|
4575
4850
|
function extractFontFaceFamilies(styles) {
|
|
@@ -4577,7 +4852,7 @@ function extractFontFaceFamilies(styles) {
|
|
|
4577
4852
|
const fontFaceRe = /@font-face\s*\{[^}]*\}/gi;
|
|
4578
4853
|
const familyRe = /font-family\s*:\s*(['"]?)([^;'"]+)\1/i;
|
|
4579
4854
|
for (const style of styles) {
|
|
4580
|
-
const content =
|
|
4855
|
+
const content = stripCssComments2(style.content);
|
|
4581
4856
|
let match;
|
|
4582
4857
|
while ((match = fontFaceRe.exec(content)) !== null) {
|
|
4583
4858
|
const familyMatch = match[0].match(familyRe);
|
|
@@ -4598,7 +4873,7 @@ function extractUsedFontFamilies(styles) {
|
|
|
4598
4873
|
const seen = /* @__PURE__ */ new Set();
|
|
4599
4874
|
const propRe = /font-family\s*:\s*([^;}{]+)/gi;
|
|
4600
4875
|
for (const style of styles) {
|
|
4601
|
-
const withoutFontFace =
|
|
4876
|
+
const withoutFontFace = stripCssComments2(style.content).replace(/@font-face\s*\{[^}]*\}/gi, "");
|
|
4602
4877
|
let match;
|
|
4603
4878
|
while ((match = propRe.exec(withoutFontFace)) !== null) {
|
|
4604
4879
|
for (const part of match[1].split(",")) {
|