@hyperframes/lint 0.8.107 → 0.8.109
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 +91 -3
- package/dist/browser.js.map +1 -1
- package/dist/index.js +91 -3
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/browser.js
CHANGED
|
@@ -122,7 +122,7 @@ function isAudibleVideoTag(tagSource) {
|
|
|
122
122
|
if (hasAttrName(tagSource, "muted")) return false;
|
|
123
123
|
if (!hasAttrName(tagSource, "data-has-audio")) return true;
|
|
124
124
|
const declared = readAttr(tagSource, "data-has-audio");
|
|
125
|
-
return declared
|
|
125
|
+
return declared === "true";
|
|
126
126
|
}
|
|
127
127
|
function mediaTimeWindow(tagSource) {
|
|
128
128
|
const start = Number(readAttr(tagSource, "data-start"));
|
|
@@ -1369,6 +1369,93 @@ function cssErrorOffset(source, error) {
|
|
|
1369
1369
|
import { validateColorGradingContract } from "@hyperframes/parsers/color-grading-contract";
|
|
1370
1370
|
import { extractMediaSrcMutations } from "@hyperframes/parsers/composition";
|
|
1371
1371
|
import { parseHTML } from "linkedom";
|
|
1372
|
+
|
|
1373
|
+
// src/rules/linkedClips.ts
|
|
1374
|
+
var SYNC_TOLERANCE_S = 1e-3;
|
|
1375
|
+
var LINKED_TIMING_FIELDS = [
|
|
1376
|
+
{ field: "start", attrs: ["data-start"], fallback: 0 },
|
|
1377
|
+
{ field: "duration", attrs: ["data-duration"], fallback: 0 },
|
|
1378
|
+
{ field: "media-start", attrs: ["data-playback-start", "data-media-start"], fallback: 0 },
|
|
1379
|
+
{ field: "playback-rate", attrs: ["data-playback-rate"], fallback: 1 }
|
|
1380
|
+
];
|
|
1381
|
+
function readTimingField(tag, attrs, fallback) {
|
|
1382
|
+
for (const attr of attrs) {
|
|
1383
|
+
const raw = readAttr(tag.raw, attr);
|
|
1384
|
+
if (raw === null || raw.trim() === "") continue;
|
|
1385
|
+
const value = Number(raw);
|
|
1386
|
+
if (Number.isFinite(value)) return value;
|
|
1387
|
+
}
|
|
1388
|
+
return fallback;
|
|
1389
|
+
}
|
|
1390
|
+
function driftingFields(members) {
|
|
1391
|
+
return LINKED_TIMING_FIELDS.filter(({ attrs, fallback }) => {
|
|
1392
|
+
const values = members.map((tag) => readTimingField(tag, attrs, fallback));
|
|
1393
|
+
const first = values[0] ?? fallback;
|
|
1394
|
+
return values.some((value) => Math.abs(value - first) > SYNC_TOLERANCE_S);
|
|
1395
|
+
}).map(({ field }) => field);
|
|
1396
|
+
}
|
|
1397
|
+
function compositionScopeOf(tag, compositions) {
|
|
1398
|
+
let scope = -1;
|
|
1399
|
+
for (const composition of compositions) {
|
|
1400
|
+
const end = composition.endIndex ?? composition.closeIndex ?? Number.POSITIVE_INFINITY;
|
|
1401
|
+
const encloses = composition.index < tag.index && tag.index < end;
|
|
1402
|
+
if (composition !== tag && encloses && composition.index > scope) scope = composition.index;
|
|
1403
|
+
}
|
|
1404
|
+
return scope;
|
|
1405
|
+
}
|
|
1406
|
+
function groupByLink(tags) {
|
|
1407
|
+
const compositions = tags.filter(
|
|
1408
|
+
(tag) => ["data-composition-id", "data-composition-file"].some(
|
|
1409
|
+
(name) => readAttr(tag.raw, name) !== null
|
|
1410
|
+
)
|
|
1411
|
+
);
|
|
1412
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1413
|
+
for (const tag of tags) {
|
|
1414
|
+
const link = readAttr(tag.raw, "data-link");
|
|
1415
|
+
if (!link) continue;
|
|
1416
|
+
const key = `${compositionScopeOf(tag, compositions)}\0${link}`;
|
|
1417
|
+
const group = groups.get(key) ?? { link, members: [] };
|
|
1418
|
+
group.members.push(tag);
|
|
1419
|
+
groups.set(key, group);
|
|
1420
|
+
}
|
|
1421
|
+
return groups;
|
|
1422
|
+
}
|
|
1423
|
+
var memberLabel = (tag) => {
|
|
1424
|
+
const id = readAttr(tag.raw, "id");
|
|
1425
|
+
return id ? `#${id}` : `<${tag.name}>`;
|
|
1426
|
+
};
|
|
1427
|
+
function findLinkedClipFindings(ctx) {
|
|
1428
|
+
const findings = [];
|
|
1429
|
+
for (const { link, members } of groupByLink(ctx.tags).values()) {
|
|
1430
|
+
const first = members[0];
|
|
1431
|
+
if (!first) continue;
|
|
1432
|
+
const elementId = readAttr(first.raw, "id") || void 0;
|
|
1433
|
+
if (members.length === 1) {
|
|
1434
|
+
findings.push({
|
|
1435
|
+
code: "linked_clip_orphan",
|
|
1436
|
+
severity: "warning",
|
|
1437
|
+
message: `${memberLabel(first)} is the only clip with data-link="${link}"; its linked partner is gone.`,
|
|
1438
|
+
elementId,
|
|
1439
|
+
fixHint: `Remove data-link="${link}" from ${memberLabel(first)}, or restore the partner clip.`,
|
|
1440
|
+
snippet: truncateSnippet(first.raw)
|
|
1441
|
+
});
|
|
1442
|
+
continue;
|
|
1443
|
+
}
|
|
1444
|
+
const drift = driftingFields(members);
|
|
1445
|
+
if (drift.length === 0) continue;
|
|
1446
|
+
findings.push({
|
|
1447
|
+
code: "linked_clips_out_of_sync",
|
|
1448
|
+
severity: "warning",
|
|
1449
|
+
message: `Linked clips ${members.map(memberLabel).join(", ")} (data-link="${link}") differ in ${drift.join(", ")}.`,
|
|
1450
|
+
elementId,
|
|
1451
|
+
fixHint: "Linked clips are edited as one: give every member the same data-start, data-duration, data-media-start and data-playback-rate, or remove data-link from all of them to unlink.",
|
|
1452
|
+
snippet: truncateSnippet(first.raw)
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
return findings;
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
// src/rules/media.ts
|
|
1372
1459
|
function tweensVolumeInSameCall(script, id) {
|
|
1373
1460
|
const selector = new RegExp(`#${escapeRegExp(id)}(?![\\w-])`, "g");
|
|
1374
1461
|
for (let hit = selector.exec(script); hit; hit = selector.exec(script)) {
|
|
@@ -2067,7 +2154,8 @@ var mediaRules = [
|
|
|
2067
2154
|
// audio_group_timing_attrs
|
|
2068
2155
|
findAudioGroupTimingAttrFindings,
|
|
2069
2156
|
// audio_group_carve_attr
|
|
2070
|
-
findAudioGroupCarveAttrFindings
|
|
2157
|
+
findAudioGroupCarveAttrFindings,
|
|
2158
|
+
findLinkedClipFindings
|
|
2071
2159
|
];
|
|
2072
2160
|
function findVolumeTweenOverridesGainFindings(ctx) {
|
|
2073
2161
|
const boosted = ctx.tags.filter((tag) => isMediaTag(tag.name)).map((tag) => ({ tag, volume: Number(readAttr(tag.raw, "data-volume") ?? "1") })).filter((entry) => Number.isFinite(entry.volume) && entry.volume !== 1).filter((entry) => !readDecodedAttr(entry.tag.raw, "data-automation")).map((entry) => ({ ...entry, id: readAttr(entry.tag.raw, "id") })).filter((entry) => Boolean(entry.id));
|
|
@@ -2156,7 +2244,7 @@ function findCarveUngroupedSourcesFindings(ctx) {
|
|
|
2156
2244
|
var AUDIO_GROUP_TIMING_ATTRS = ["data-start", "data-duration", "data-track-index"];
|
|
2157
2245
|
function findAudioGroupNoMembersFindings(ctx) {
|
|
2158
2246
|
const memberGroupIds = new Set(
|
|
2159
|
-
ctx.tags.filter((tag) => tag.name === "audio").map((tag) => readAttr(tag.raw, "data-audio-group")).filter((id) => Boolean(id))
|
|
2247
|
+
ctx.tags.filter((tag) => tag.name === "audio" || tag.name === "video" && isAudibleVideoTag(tag.raw)).map((tag) => readAttr(tag.raw, "data-audio-group")).filter((id) => Boolean(id))
|
|
2160
2248
|
);
|
|
2161
2249
|
if (memberGroupIds.size === 0) return [];
|
|
2162
2250
|
const mayHaveCrossFileMembers = ctx.tags.some(
|