@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/browser.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
|
+
}
|
|
220
|
+
} else if (ch === "\\") {
|
|
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);
|
|
203
241
|
} else if (ch === "\\") {
|
|
204
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
|
}
|
|
@@ -803,6 +1057,12 @@ var VIDEO_SRC_EXT = /* @__PURE__ */ new Set([
|
|
|
803
1057
|
"mpg",
|
|
804
1058
|
"mpeg"
|
|
805
1059
|
]);
|
|
1060
|
+
var AUDIO_SRC_EXT = /* @__PURE__ */ new Set(["mp3", "wav", "aac", "flac", "opus", "aiff", "wma"]);
|
|
1061
|
+
var SRC_KIND_NOUN = {
|
|
1062
|
+
image: "an image",
|
|
1063
|
+
video: "a video",
|
|
1064
|
+
audio: "an audio file"
|
|
1065
|
+
};
|
|
806
1066
|
function srcKind(src) {
|
|
807
1067
|
const stripped = src.trim();
|
|
808
1068
|
if (!stripped) return null;
|
|
@@ -812,6 +1072,7 @@ function srcKind(src) {
|
|
|
812
1072
|
if (!mime) return null;
|
|
813
1073
|
if (mime.startsWith("image/")) return "image";
|
|
814
1074
|
if (mime.startsWith("video/")) return "video";
|
|
1075
|
+
if (mime.startsWith("audio/")) return "audio";
|
|
815
1076
|
return null;
|
|
816
1077
|
}
|
|
817
1078
|
if (lower.startsWith("blob:")) return null;
|
|
@@ -831,6 +1092,7 @@ function srcKind(src) {
|
|
|
831
1092
|
const ext = base.slice(dot + 1).toLowerCase();
|
|
832
1093
|
if (IMAGE_SRC_EXT.has(ext)) return "image";
|
|
833
1094
|
if (VIDEO_SRC_EXT.has(ext)) return "video";
|
|
1095
|
+
if (AUDIO_SRC_EXT.has(ext)) return "audio";
|
|
834
1096
|
return null;
|
|
835
1097
|
}
|
|
836
1098
|
function findMediaSrcKindMismatchFindings(ctx) {
|
|
@@ -841,16 +1103,15 @@ function findMediaSrcKindMismatchFindings(ctx) {
|
|
|
841
1103
|
if (!src) continue;
|
|
842
1104
|
const kind = srcKind(src);
|
|
843
1105
|
if (kind === null) continue;
|
|
844
|
-
if (tag.name === "video" && kind !== "image") continue;
|
|
845
|
-
if (tag.name === "img" && kind !== "video") continue;
|
|
846
|
-
const elementId = readAttr(tag.raw, "id") || void 0;
|
|
847
1106
|
const expected = tag.name === "video" ? "video" : "image";
|
|
1107
|
+
if (kind === expected) continue;
|
|
1108
|
+
const elementId = readAttr(tag.raw, "id") || void 0;
|
|
848
1109
|
findings.push({
|
|
849
1110
|
code: "media_src_kind_mismatch",
|
|
850
1111
|
severity: "error",
|
|
851
|
-
message: `<${tag.name}${elementId ? ` id="${elementId}"` : ""}> src is
|
|
1112
|
+
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.`,
|
|
852
1113
|
elementId,
|
|
853
|
-
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).",
|
|
1114
|
+
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).",
|
|
854
1115
|
snippet: truncateSnippet(tag.raw)
|
|
855
1116
|
});
|
|
856
1117
|
}
|
|
@@ -1635,6 +1896,14 @@ function isHiddenGsapState(values) {
|
|
|
1635
1896
|
const display = stringValue(values.display)?.toLowerCase();
|
|
1636
1897
|
return zeroValue(values.opacity) || zeroValue(values.autoAlpha) || visibility === "hidden" || display === "none";
|
|
1637
1898
|
}
|
|
1899
|
+
function hiddenSetTargetSelectors(target, aliases) {
|
|
1900
|
+
const parts = target.startsWith("[") && target.endsWith("]") ? target.slice(1, -1).split(",") : [target];
|
|
1901
|
+
return parts.flatMap((part) => {
|
|
1902
|
+
const trimmed = part.trim();
|
|
1903
|
+
const resolved = /^(["'`])([^"'`]+)\1$/.exec(trimmed)?.[2] ?? aliases.get(trimmed);
|
|
1904
|
+
return resolved === void 0 ? [] : resolved.split(",");
|
|
1905
|
+
}).map((selector) => selector.trim()).filter((selector) => selector.length > 0);
|
|
1906
|
+
}
|
|
1638
1907
|
function extractStandaloneHiddenSelectors(script) {
|
|
1639
1908
|
const selectors = /* @__PURE__ */ new Set();
|
|
1640
1909
|
const source = stripJsComments(script);
|
|
@@ -1645,16 +1914,15 @@ function extractStandaloneHiddenSelectors(script) {
|
|
|
1645
1914
|
)) {
|
|
1646
1915
|
aliases.set(match2[1] ?? "", match2[3] ?? "");
|
|
1647
1916
|
}
|
|
1648
|
-
const pattern = /gsap\.set\s*\(\s*([^,]+?)\s*,\s*\{([\s\S]*?)\}\s*\)/g;
|
|
1917
|
+
const pattern = /gsap\.set\s*\(\s*(\[[^[\]]*\]|"[^"]*"|'[^']*'|`[^`]*`|[^,()[\]]+?)\s*,\s*\{([\s\S]*?)\}\s*\)/g;
|
|
1649
1918
|
let match;
|
|
1650
1919
|
while ((match = pattern.exec(source)) !== null) {
|
|
1651
1920
|
if (indexInsideNonIifeRange(match.index, source, functionRanges)) continue;
|
|
1652
|
-
const
|
|
1653
|
-
|
|
1654
|
-
if (!selector) continue;
|
|
1921
|
+
const targets = hiddenSetTargetSelectors((match[1] ?? "").trim(), aliases);
|
|
1922
|
+
if (targets.length === 0) continue;
|
|
1655
1923
|
const body = match[2] ?? "";
|
|
1656
1924
|
if (/(?:opacity|autoAlpha)\s*:\s*0(?:\.0+)?\s*(?:,|$)/.test(body)) {
|
|
1657
|
-
selectors.add(selector);
|
|
1925
|
+
for (const selector of targets) selectors.add(selector);
|
|
1658
1926
|
}
|
|
1659
1927
|
}
|
|
1660
1928
|
return selectors;
|
|
@@ -2234,6 +2502,9 @@ var gsapRules = [
|
|
|
2234
2502
|
// fallow-ignore-next-line complexity
|
|
2235
2503
|
async ({ source, tags, scripts, styles, rootCompositionId }) => {
|
|
2236
2504
|
const findings = [];
|
|
2505
|
+
const authoredHiddenSelectors = new Set(
|
|
2506
|
+
scripts.flatMap((script) => [...extractStandaloneHiddenSelectors(script.content)])
|
|
2507
|
+
);
|
|
2237
2508
|
const clipIds = /* @__PURE__ */ new Map();
|
|
2238
2509
|
const clipClasses = /* @__PURE__ */ new Map();
|
|
2239
2510
|
for (const tag of tags) {
|
|
@@ -2326,9 +2597,10 @@ ${right.raw}`)
|
|
|
2326
2597
|
);
|
|
2327
2598
|
}).sort((a, b) => a.position - b.position);
|
|
2328
2599
|
const startsHiddenAtZero = visibilityWindows.some(
|
|
2329
|
-
(win) => win.position <= SCENE_BOUNDARY_EPSILON_SECONDS && isHiddenGsapState(win.propertyValues)
|
|
2600
|
+
(win) => win.position <= SCENE_BOUNDARY_EPSILON_SECONDS && (isHiddenGsapState(win.propertyValues) || win.fromPropertyValues !== void 0 && isHiddenGsapState(win.fromPropertyValues))
|
|
2330
2601
|
);
|
|
2331
2602
|
if (startsHiddenAtZero) continue;
|
|
2603
|
+
if (selectors.some((selector2) => authoredHiddenSelectors.has(selector2))) continue;
|
|
2332
2604
|
const firstVisible = visibilityWindows.find((win) => makesOverlayVisible(win));
|
|
2333
2605
|
if (!firstVisible) continue;
|
|
2334
2606
|
const selector = selectors.find(
|
|
@@ -3796,8 +4068,8 @@ var compositionRules = [
|
|
|
3796
4068
|
});
|
|
3797
4069
|
}
|
|
3798
4070
|
};
|
|
3799
|
-
for (const style of styles) scan(style.content);
|
|
3800
|
-
for (const script of scripts) scan(script.content);
|
|
4071
|
+
for (const style of styles) scan(stripCssComments(style.content));
|
|
4072
|
+
for (const script of scripts) scan(stripJsComments(script.content));
|
|
3801
4073
|
return findings;
|
|
3802
4074
|
},
|
|
3803
4075
|
// template_literal_selector
|
|
@@ -3805,14 +4077,17 @@ var compositionRules = [
|
|
|
3805
4077
|
const findings = [];
|
|
3806
4078
|
for (const script of scripts) {
|
|
3807
4079
|
const templateLiteralSelectorPattern = /(?:querySelector|querySelectorAll)\s*\(\s*`[^`]*\$\{[^}]+\}[^`]*`\s*\)/g;
|
|
4080
|
+
const scanned = stripJsCode(script.content);
|
|
3808
4081
|
let tlMatch;
|
|
3809
|
-
while ((tlMatch = templateLiteralSelectorPattern.exec(
|
|
4082
|
+
while ((tlMatch = templateLiteralSelectorPattern.exec(scanned)) !== null) {
|
|
3810
4083
|
findings.push({
|
|
3811
4084
|
code: "template_literal_selector",
|
|
3812
4085
|
severity: "error",
|
|
3813
4086
|
message: "querySelector uses a template literal variable (e.g. `${compId}`). The HTML bundler's CSS parser crashes on these. Use a hardcoded string instead.",
|
|
3814
4087
|
fixHint: "Replace the template literal variable with a hardcoded string. The bundler's CSS parser cannot handle interpolated variables in script content.",
|
|
3815
|
-
snippet: truncateSnippet(
|
|
4088
|
+
snippet: truncateSnippet(
|
|
4089
|
+
script.content.slice(tlMatch.index, tlMatch.index + tlMatch[0].length)
|
|
4090
|
+
)
|
|
3816
4091
|
});
|
|
3817
4092
|
}
|
|
3818
4093
|
}
|
|
@@ -3914,7 +4189,7 @@ var compositionRules = [
|
|
|
3914
4189
|
if (isRegistrySourceFile(options.filePath) || isRegistryInstalledFile(rawSource)) return [];
|
|
3915
4190
|
const findings = [];
|
|
3916
4191
|
for (const script of scripts) {
|
|
3917
|
-
const stripped =
|
|
4192
|
+
const stripped = stripJsCode(script.content);
|
|
3918
4193
|
if (/requestAnimationFrame\s*\(/.test(stripped)) {
|
|
3919
4194
|
findings.push({
|
|
3920
4195
|
code: "requestanimationframe_in_composition",
|
|
@@ -4566,7 +4841,7 @@ var GENERIC_FAMILIES = /* @__PURE__ */ new Set([
|
|
|
4566
4841
|
"unset",
|
|
4567
4842
|
"revert"
|
|
4568
4843
|
]);
|
|
4569
|
-
function
|
|
4844
|
+
function stripCssComments2(css) {
|
|
4570
4845
|
return css.replace(/\/\*[\s\S]*?\*\//g, " ");
|
|
4571
4846
|
}
|
|
4572
4847
|
function extractFontFaceFamilies(styles) {
|
|
@@ -4574,7 +4849,7 @@ function extractFontFaceFamilies(styles) {
|
|
|
4574
4849
|
const fontFaceRe = /@font-face\s*\{[^}]*\}/gi;
|
|
4575
4850
|
const familyRe = /font-family\s*:\s*(['"]?)([^;'"]+)\1/i;
|
|
4576
4851
|
for (const style of styles) {
|
|
4577
|
-
const content =
|
|
4852
|
+
const content = stripCssComments2(style.content);
|
|
4578
4853
|
let match;
|
|
4579
4854
|
while ((match = fontFaceRe.exec(content)) !== null) {
|
|
4580
4855
|
const familyMatch = match[0].match(familyRe);
|
|
@@ -4595,7 +4870,7 @@ function extractUsedFontFamilies(styles) {
|
|
|
4595
4870
|
const seen = /* @__PURE__ */ new Set();
|
|
4596
4871
|
const propRe = /font-family\s*:\s*([^;}{]+)/gi;
|
|
4597
4872
|
for (const style of styles) {
|
|
4598
|
-
const withoutFontFace =
|
|
4873
|
+
const withoutFontFace = stripCssComments2(style.content).replace(/@font-face\s*\{[^}]*\}/gi, "");
|
|
4599
4874
|
let match;
|
|
4600
4875
|
while ((match = propRe.exec(withoutFontFace)) !== null) {
|
|
4601
4876
|
for (const part of match[1].split(",")) {
|