@hyperframes/lint 0.8.166 → 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 +37 -33
- package/dist/index.js +37 -33
- package/package.json +2 -2
package/dist/browser.js
CHANGED
|
@@ -2740,8 +2740,9 @@ function stylesheetCanvasZoomHits(styles, rootId, rootClasses) {
|
|
|
2740
2740
|
const hits = [];
|
|
2741
2741
|
for (const style of styles) {
|
|
2742
2742
|
const ruleWithBody = /([^{}]+)\{([^{}]*)\}/g;
|
|
2743
|
+
const content = stripCssComments(style.content);
|
|
2743
2744
|
let match;
|
|
2744
|
-
while ((match = ruleWithBody.exec(
|
|
2745
|
+
while ((match = ruleWithBody.exec(content)) !== null) {
|
|
2745
2746
|
const hit = canvasZoomInRule(match[1] ?? "", match[2] ?? "", rootId, rootClasses);
|
|
2746
2747
|
if (hit) hits.push(hit);
|
|
2747
2748
|
}
|
|
@@ -3387,37 +3388,6 @@ var compositionRules = [
|
|
|
3387
3388
|
// defaults aren't applied.
|
|
3388
3389
|
// Checked on every element that declares: <html>, or the composition root.
|
|
3389
3390
|
({ tags }) => tags.filter((tag) => readJsonAttr(tag.raw, "data-composition-variables")).flatMap((tag) => variablesDeclarationFindings(tag, tags)),
|
|
3390
|
-
// html_dir_attribute_breaks_render — valid non-LTR dir values on
|
|
3391
|
-
// <html> renders correctly in preview/snapshot but produces a fully
|
|
3392
|
-
// blank/black video from render, with no other lint/validate/inspect
|
|
3393
|
-
// check catching it (output file size, far smaller than expected, is the
|
|
3394
|
-
// only tell). Confirmed independently by two separate reports, both
|
|
3395
|
-
// diagnosing the same exact trigger and the same fix: drop dir from
|
|
3396
|
-
// <html>, keep lang, and scope `direction: rtl` to individual
|
|
3397
|
-
// text-containing elements via CSS instead (text still bidi-shapes
|
|
3398
|
-
// correctly). Advisory-only — this does not attempt to fix the render
|
|
3399
|
-
// pipeline's own root cause (suspected to be a capture step that clips a
|
|
3400
|
-
// fixed top-left-origin screenshot region, which RTL layout can shift the
|
|
3401
|
-
// actual content away from), only surfaces the already-confirmed footgun
|
|
3402
|
-
// before someone hits it blind.
|
|
3403
|
-
({ tags }) => {
|
|
3404
|
-
const htmlTag = findHtmlTag(tags);
|
|
3405
|
-
if (!htmlTag) return [];
|
|
3406
|
-
const dir = readAttr(htmlTag.raw, "dir");
|
|
3407
|
-
if (!dir) return [];
|
|
3408
|
-
const normalizedDir = dir.toLowerCase();
|
|
3409
|
-
if (normalizedDir !== "rtl" && normalizedDir !== "auto") return [];
|
|
3410
|
-
const scopedDirection = normalizedDir === "auto" ? 'dir="auto"' : `direction: ${normalizedDir}`;
|
|
3411
|
-
return [
|
|
3412
|
-
{
|
|
3413
|
-
code: "html_dir_attribute_breaks_render",
|
|
3414
|
-
severity: "error",
|
|
3415
|
-
message: `<html dir="${dir}"> renders correctly in preview/snapshot but produces a fully blank/black video from render. This is a confirmed, silent failure.`,
|
|
3416
|
-
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.`,
|
|
3417
|
-
snippet: truncateSnippet(htmlTag.raw)
|
|
3418
|
-
}
|
|
3419
|
-
];
|
|
3420
|
-
},
|
|
3421
3391
|
// subcomposition_blanks_before_host
|
|
3422
3392
|
// Warns when a full-bleed sub-composition slot ends before the host composition
|
|
3423
3393
|
// does, leaving the slot blank for the remainder (issue #1540). Scoped narrowly to
|
|
@@ -6243,7 +6213,8 @@ var NODE_ATTRS = [
|
|
|
6243
6213
|
"data-track-index",
|
|
6244
6214
|
"data-track-kind",
|
|
6245
6215
|
"data-composition-id",
|
|
6246
|
-
"data-composition-src"
|
|
6216
|
+
"data-composition-src",
|
|
6217
|
+
"data-hf-media-start-basis"
|
|
6247
6218
|
];
|
|
6248
6219
|
function toNode(open) {
|
|
6249
6220
|
const attrs = {};
|
|
@@ -6351,6 +6322,33 @@ function clipsPastRootFindings(root, rows) {
|
|
|
6351
6322
|
];
|
|
6352
6323
|
});
|
|
6353
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
|
+
}
|
|
6354
6352
|
var structureRules = [
|
|
6355
6353
|
(ctx) => {
|
|
6356
6354
|
if (ctx.options.isSubComposition) return [];
|
|
@@ -6364,6 +6362,12 @@ var structureRules = [
|
|
|
6364
6362
|
...captionFindings(rows, severity),
|
|
6365
6363
|
...clipsPastRootFindings(root, rows)
|
|
6366
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));
|
|
6367
6371
|
}
|
|
6368
6372
|
];
|
|
6369
6373
|
|
package/dist/index.js
CHANGED
|
@@ -2746,8 +2746,9 @@ function stylesheetCanvasZoomHits(styles, rootId, rootClasses) {
|
|
|
2746
2746
|
const hits = [];
|
|
2747
2747
|
for (const style of styles) {
|
|
2748
2748
|
const ruleWithBody = /([^{}]+)\{([^{}]*)\}/g;
|
|
2749
|
+
const content = stripCssComments(style.content);
|
|
2749
2750
|
let match;
|
|
2750
|
-
while ((match = ruleWithBody.exec(
|
|
2751
|
+
while ((match = ruleWithBody.exec(content)) !== null) {
|
|
2751
2752
|
const hit = canvasZoomInRule(match[1] ?? "", match[2] ?? "", rootId, rootClasses);
|
|
2752
2753
|
if (hit) hits.push(hit);
|
|
2753
2754
|
}
|
|
@@ -3393,37 +3394,6 @@ var compositionRules = [
|
|
|
3393
3394
|
// defaults aren't applied.
|
|
3394
3395
|
// Checked on every element that declares: <html>, or the composition root.
|
|
3395
3396
|
({ tags }) => tags.filter((tag) => readJsonAttr(tag.raw, "data-composition-variables")).flatMap((tag) => variablesDeclarationFindings(tag, tags)),
|
|
3396
|
-
// html_dir_attribute_breaks_render — valid non-LTR dir values on
|
|
3397
|
-
// <html> renders correctly in preview/snapshot but produces a fully
|
|
3398
|
-
// blank/black video from render, with no other lint/validate/inspect
|
|
3399
|
-
// check catching it (output file size, far smaller than expected, is the
|
|
3400
|
-
// only tell). Confirmed independently by two separate reports, both
|
|
3401
|
-
// diagnosing the same exact trigger and the same fix: drop dir from
|
|
3402
|
-
// <html>, keep lang, and scope `direction: rtl` to individual
|
|
3403
|
-
// text-containing elements via CSS instead (text still bidi-shapes
|
|
3404
|
-
// correctly). Advisory-only — this does not attempt to fix the render
|
|
3405
|
-
// pipeline's own root cause (suspected to be a capture step that clips a
|
|
3406
|
-
// fixed top-left-origin screenshot region, which RTL layout can shift the
|
|
3407
|
-
// actual content away from), only surfaces the already-confirmed footgun
|
|
3408
|
-
// before someone hits it blind.
|
|
3409
|
-
({ tags }) => {
|
|
3410
|
-
const htmlTag = findHtmlTag(tags);
|
|
3411
|
-
if (!htmlTag) return [];
|
|
3412
|
-
const dir = readAttr(htmlTag.raw, "dir");
|
|
3413
|
-
if (!dir) return [];
|
|
3414
|
-
const normalizedDir = dir.toLowerCase();
|
|
3415
|
-
if (normalizedDir !== "rtl" && normalizedDir !== "auto") return [];
|
|
3416
|
-
const scopedDirection = normalizedDir === "auto" ? 'dir="auto"' : `direction: ${normalizedDir}`;
|
|
3417
|
-
return [
|
|
3418
|
-
{
|
|
3419
|
-
code: "html_dir_attribute_breaks_render",
|
|
3420
|
-
severity: "error",
|
|
3421
|
-
message: `<html dir="${dir}"> renders correctly in preview/snapshot but produces a fully blank/black video from render. This is a confirmed, silent failure.`,
|
|
3422
|
-
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.`,
|
|
3423
|
-
snippet: truncateSnippet(htmlTag.raw)
|
|
3424
|
-
}
|
|
3425
|
-
];
|
|
3426
|
-
},
|
|
3427
3397
|
// subcomposition_blanks_before_host
|
|
3428
3398
|
// Warns when a full-bleed sub-composition slot ends before the host composition
|
|
3429
3399
|
// does, leaving the slot blank for the remainder (issue #1540). Scoped narrowly to
|
|
@@ -6249,7 +6219,8 @@ var NODE_ATTRS = [
|
|
|
6249
6219
|
"data-track-index",
|
|
6250
6220
|
"data-track-kind",
|
|
6251
6221
|
"data-composition-id",
|
|
6252
|
-
"data-composition-src"
|
|
6222
|
+
"data-composition-src",
|
|
6223
|
+
"data-hf-media-start-basis"
|
|
6253
6224
|
];
|
|
6254
6225
|
function toNode(open) {
|
|
6255
6226
|
const attrs = {};
|
|
@@ -6357,6 +6328,33 @@ function clipsPastRootFindings(root, rows) {
|
|
|
6357
6328
|
];
|
|
6358
6329
|
});
|
|
6359
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
|
+
}
|
|
6360
6358
|
var structureRules = [
|
|
6361
6359
|
(ctx) => {
|
|
6362
6360
|
if (ctx.options.isSubComposition) return [];
|
|
@@ -6370,6 +6368,12 @@ var structureRules = [
|
|
|
6370
6368
|
...captionFindings(rows, severity),
|
|
6371
6369
|
...clipsPastRootFindings(root, rows)
|
|
6372
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));
|
|
6373
6377
|
}
|
|
6374
6378
|
];
|
|
6375
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",
|