@hyperframes/lint 0.8.167 → 0.8.168
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 +35 -32
- package/dist/index.js +35 -32
- package/package.json +2 -2
package/dist/browser.js
CHANGED
|
@@ -3388,37 +3388,6 @@ var compositionRules = [
|
|
|
3388
3388
|
// defaults aren't applied.
|
|
3389
3389
|
// Checked on every element that declares: <html>, or the composition root.
|
|
3390
3390
|
({ tags }) => tags.filter((tag) => readJsonAttr(tag.raw, "data-composition-variables")).flatMap((tag) => variablesDeclarationFindings(tag, tags)),
|
|
3391
|
-
// html_dir_attribute_breaks_render — valid non-LTR dir values on
|
|
3392
|
-
// <html> renders correctly in preview/snapshot but produces a fully
|
|
3393
|
-
// blank/black video from render, with no other lint/validate/inspect
|
|
3394
|
-
// check catching it (output file size, far smaller than expected, is the
|
|
3395
|
-
// only tell). Confirmed independently by two separate reports, both
|
|
3396
|
-
// diagnosing the same exact trigger and the same fix: drop dir from
|
|
3397
|
-
// <html>, keep lang, and scope `direction: rtl` to individual
|
|
3398
|
-
// text-containing elements via CSS instead (text still bidi-shapes
|
|
3399
|
-
// correctly). Advisory-only — this does not attempt to fix the render
|
|
3400
|
-
// pipeline's own root cause (suspected to be a capture step that clips a
|
|
3401
|
-
// fixed top-left-origin screenshot region, which RTL layout can shift the
|
|
3402
|
-
// actual content away from), only surfaces the already-confirmed footgun
|
|
3403
|
-
// before someone hits it blind.
|
|
3404
|
-
({ tags }) => {
|
|
3405
|
-
const htmlTag = findHtmlTag(tags);
|
|
3406
|
-
if (!htmlTag) return [];
|
|
3407
|
-
const dir = readAttr(htmlTag.raw, "dir");
|
|
3408
|
-
if (!dir) return [];
|
|
3409
|
-
const normalizedDir = dir.toLowerCase();
|
|
3410
|
-
if (normalizedDir !== "rtl" && normalizedDir !== "auto") return [];
|
|
3411
|
-
const scopedDirection = normalizedDir === "auto" ? 'dir="auto"' : `direction: ${normalizedDir}`;
|
|
3412
|
-
return [
|
|
3413
|
-
{
|
|
3414
|
-
code: "html_dir_attribute_breaks_render",
|
|
3415
|
-
severity: "error",
|
|
3416
|
-
message: `<html dir="${dir}"> renders correctly in preview/snapshot but produces a fully blank/black video from render. This is a confirmed, silent failure.`,
|
|
3417
|
-
fixHint: `Remove dir="${dir}" from <html>. Keep lang, and scope ${scopedDirection} to individual text-containing elements instead. Text still shapes correctly via the browser's own bidi algorithm.`,
|
|
3418
|
-
snippet: truncateSnippet(htmlTag.raw)
|
|
3419
|
-
}
|
|
3420
|
-
];
|
|
3421
|
-
},
|
|
3422
3391
|
// subcomposition_blanks_before_host
|
|
3423
3392
|
// Warns when a full-bleed sub-composition slot ends before the host composition
|
|
3424
3393
|
// does, leaving the slot blank for the remainder (issue #1540). Scoped narrowly to
|
|
@@ -6244,7 +6213,8 @@ var NODE_ATTRS = [
|
|
|
6244
6213
|
"data-track-index",
|
|
6245
6214
|
"data-track-kind",
|
|
6246
6215
|
"data-composition-id",
|
|
6247
|
-
"data-composition-src"
|
|
6216
|
+
"data-composition-src",
|
|
6217
|
+
"data-hf-media-start-basis"
|
|
6248
6218
|
];
|
|
6249
6219
|
function toNode(open) {
|
|
6250
6220
|
const attrs = {};
|
|
@@ -6352,6 +6322,33 @@ function clipsPastRootFindings(root, rows) {
|
|
|
6352
6322
|
];
|
|
6353
6323
|
});
|
|
6354
6324
|
}
|
|
6325
|
+
function followsParentVisibility(clip) {
|
|
6326
|
+
return clip.tag !== "audio" && clip.attrs["data-hf-media-start-basis"] !== "global";
|
|
6327
|
+
}
|
|
6328
|
+
function isTimed(timing) {
|
|
6329
|
+
return timing.start !== null && timing.end !== null;
|
|
6330
|
+
}
|
|
6331
|
+
function neverVisibleNestedFindings(parent, severity) {
|
|
6332
|
+
const window = readClipTiming2({ getAttribute: (name) => parent.attrs[name] ?? null });
|
|
6333
|
+
return topLevelElements(parent).flatMap((clip) => {
|
|
6334
|
+
const inner = isSubCompositionHost(clip) ? [] : neverVisibleNestedFindings(clip, severity);
|
|
6335
|
+
const own = readClipTiming2({ getAttribute: (name) => clip.attrs[name] ?? null });
|
|
6336
|
+
if (!followsParentVisibility(clip) || !isTimed(window) || !isTimed(own)) return inner;
|
|
6337
|
+
if (own.end > window.start && own.start < window.end) return inner;
|
|
6338
|
+
const shows = `${hundredths(window.start)}s to ${hundredths(window.end)}s`;
|
|
6339
|
+
return [
|
|
6340
|
+
{
|
|
6341
|
+
code: "nested_clip_never_visible",
|
|
6342
|
+
severity,
|
|
6343
|
+
message: `${describe(clip)} plays from ${hundredths(own.start)}s to ${hundredths(own.end)}s, but it sits inside ${describe(parent)}, which only shows from ${shows}, so it never appears. A nested data-start counts from the composition's start, not from ${describe(parent)}.`,
|
|
6344
|
+
elementId: clip.attrs.id,
|
|
6345
|
+
fixHint: `Give ${describe(clip)} a data-start between ${shows} in composition time, or move it out of ${describe(parent)}.`,
|
|
6346
|
+
snippet: truncateSnippet(clip.open.raw)
|
|
6347
|
+
},
|
|
6348
|
+
...inner
|
|
6349
|
+
];
|
|
6350
|
+
});
|
|
6351
|
+
}
|
|
6355
6352
|
var structureRules = [
|
|
6356
6353
|
(ctx) => {
|
|
6357
6354
|
if (ctx.options.isSubComposition) return [];
|
|
@@ -6365,6 +6362,12 @@ var structureRules = [
|
|
|
6365
6362
|
...captionFindings(rows, severity),
|
|
6366
6363
|
...clipsPastRootFindings(root, rows)
|
|
6367
6364
|
];
|
|
6365
|
+
},
|
|
6366
|
+
(ctx) => {
|
|
6367
|
+
const root = buildTree(ctx);
|
|
6368
|
+
if (!root) return [];
|
|
6369
|
+
const severity = ctx.options.host === "studio" ? "error" : "warning";
|
|
6370
|
+
return topLevelElements(root).filter((row) => !isSubCompositionHost(row)).flatMap((row) => neverVisibleNestedFindings(row, severity));
|
|
6368
6371
|
}
|
|
6369
6372
|
];
|
|
6370
6373
|
|
package/dist/index.js
CHANGED
|
@@ -3394,37 +3394,6 @@ var compositionRules = [
|
|
|
3394
3394
|
// defaults aren't applied.
|
|
3395
3395
|
// Checked on every element that declares: <html>, or the composition root.
|
|
3396
3396
|
({ tags }) => tags.filter((tag) => readJsonAttr(tag.raw, "data-composition-variables")).flatMap((tag) => variablesDeclarationFindings(tag, tags)),
|
|
3397
|
-
// html_dir_attribute_breaks_render — valid non-LTR dir values on
|
|
3398
|
-
// <html> renders correctly in preview/snapshot but produces a fully
|
|
3399
|
-
// blank/black video from render, with no other lint/validate/inspect
|
|
3400
|
-
// check catching it (output file size, far smaller than expected, is the
|
|
3401
|
-
// only tell). Confirmed independently by two separate reports, both
|
|
3402
|
-
// diagnosing the same exact trigger and the same fix: drop dir from
|
|
3403
|
-
// <html>, keep lang, and scope `direction: rtl` to individual
|
|
3404
|
-
// text-containing elements via CSS instead (text still bidi-shapes
|
|
3405
|
-
// correctly). Advisory-only — this does not attempt to fix the render
|
|
3406
|
-
// pipeline's own root cause (suspected to be a capture step that clips a
|
|
3407
|
-
// fixed top-left-origin screenshot region, which RTL layout can shift the
|
|
3408
|
-
// actual content away from), only surfaces the already-confirmed footgun
|
|
3409
|
-
// before someone hits it blind.
|
|
3410
|
-
({ tags }) => {
|
|
3411
|
-
const htmlTag = findHtmlTag(tags);
|
|
3412
|
-
if (!htmlTag) return [];
|
|
3413
|
-
const dir = readAttr(htmlTag.raw, "dir");
|
|
3414
|
-
if (!dir) return [];
|
|
3415
|
-
const normalizedDir = dir.toLowerCase();
|
|
3416
|
-
if (normalizedDir !== "rtl" && normalizedDir !== "auto") return [];
|
|
3417
|
-
const scopedDirection = normalizedDir === "auto" ? 'dir="auto"' : `direction: ${normalizedDir}`;
|
|
3418
|
-
return [
|
|
3419
|
-
{
|
|
3420
|
-
code: "html_dir_attribute_breaks_render",
|
|
3421
|
-
severity: "error",
|
|
3422
|
-
message: `<html dir="${dir}"> renders correctly in preview/snapshot but produces a fully blank/black video from render. This is a confirmed, silent failure.`,
|
|
3423
|
-
fixHint: `Remove dir="${dir}" from <html>. Keep lang, and scope ${scopedDirection} to individual text-containing elements instead. Text still shapes correctly via the browser's own bidi algorithm.`,
|
|
3424
|
-
snippet: truncateSnippet(htmlTag.raw)
|
|
3425
|
-
}
|
|
3426
|
-
];
|
|
3427
|
-
},
|
|
3428
3397
|
// subcomposition_blanks_before_host
|
|
3429
3398
|
// Warns when a full-bleed sub-composition slot ends before the host composition
|
|
3430
3399
|
// does, leaving the slot blank for the remainder (issue #1540). Scoped narrowly to
|
|
@@ -6250,7 +6219,8 @@ var NODE_ATTRS = [
|
|
|
6250
6219
|
"data-track-index",
|
|
6251
6220
|
"data-track-kind",
|
|
6252
6221
|
"data-composition-id",
|
|
6253
|
-
"data-composition-src"
|
|
6222
|
+
"data-composition-src",
|
|
6223
|
+
"data-hf-media-start-basis"
|
|
6254
6224
|
];
|
|
6255
6225
|
function toNode(open) {
|
|
6256
6226
|
const attrs = {};
|
|
@@ -6358,6 +6328,33 @@ function clipsPastRootFindings(root, rows) {
|
|
|
6358
6328
|
];
|
|
6359
6329
|
});
|
|
6360
6330
|
}
|
|
6331
|
+
function followsParentVisibility(clip) {
|
|
6332
|
+
return clip.tag !== "audio" && clip.attrs["data-hf-media-start-basis"] !== "global";
|
|
6333
|
+
}
|
|
6334
|
+
function isTimed(timing) {
|
|
6335
|
+
return timing.start !== null && timing.end !== null;
|
|
6336
|
+
}
|
|
6337
|
+
function neverVisibleNestedFindings(parent, severity) {
|
|
6338
|
+
const window = readClipTiming2({ getAttribute: (name) => parent.attrs[name] ?? null });
|
|
6339
|
+
return topLevelElements(parent).flatMap((clip) => {
|
|
6340
|
+
const inner = isSubCompositionHost(clip) ? [] : neverVisibleNestedFindings(clip, severity);
|
|
6341
|
+
const own = readClipTiming2({ getAttribute: (name) => clip.attrs[name] ?? null });
|
|
6342
|
+
if (!followsParentVisibility(clip) || !isTimed(window) || !isTimed(own)) return inner;
|
|
6343
|
+
if (own.end > window.start && own.start < window.end) return inner;
|
|
6344
|
+
const shows = `${hundredths(window.start)}s to ${hundredths(window.end)}s`;
|
|
6345
|
+
return [
|
|
6346
|
+
{
|
|
6347
|
+
code: "nested_clip_never_visible",
|
|
6348
|
+
severity,
|
|
6349
|
+
message: `${describe(clip)} plays from ${hundredths(own.start)}s to ${hundredths(own.end)}s, but it sits inside ${describe(parent)}, which only shows from ${shows}, so it never appears. A nested data-start counts from the composition's start, not from ${describe(parent)}.`,
|
|
6350
|
+
elementId: clip.attrs.id,
|
|
6351
|
+
fixHint: `Give ${describe(clip)} a data-start between ${shows} in composition time, or move it out of ${describe(parent)}.`,
|
|
6352
|
+
snippet: truncateSnippet(clip.open.raw)
|
|
6353
|
+
},
|
|
6354
|
+
...inner
|
|
6355
|
+
];
|
|
6356
|
+
});
|
|
6357
|
+
}
|
|
6361
6358
|
var structureRules = [
|
|
6362
6359
|
(ctx) => {
|
|
6363
6360
|
if (ctx.options.isSubComposition) return [];
|
|
@@ -6371,6 +6368,12 @@ var structureRules = [
|
|
|
6371
6368
|
...captionFindings(rows, severity),
|
|
6372
6369
|
...clipsPastRootFindings(root, rows)
|
|
6373
6370
|
];
|
|
6371
|
+
},
|
|
6372
|
+
(ctx) => {
|
|
6373
|
+
const root = buildTree(ctx);
|
|
6374
|
+
if (!root) return [];
|
|
6375
|
+
const severity = ctx.options.host === "studio" ? "error" : "warning";
|
|
6376
|
+
return topLevelElements(root).filter((row) => !isSubCompositionHost(row)).flatMap((row) => neverVisibleNestedFindings(row, severity));
|
|
6374
6377
|
}
|
|
6375
6378
|
];
|
|
6376
6379
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/lint",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.168",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/heygen-com/hyperframes",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"linkedom": "^0.18.12",
|
|
36
36
|
"postcss": "^8.5.8",
|
|
37
37
|
"postcss-selector-parser": "^7.1.4",
|
|
38
|
-
"@hyperframes/parsers": "0.8.
|
|
38
|
+
"@hyperframes/parsers": "0.8.168"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "^25.0.10",
|