@hyperframes/lint 0.8.206 → 0.8.208
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 +81 -1
- package/dist/index.js +101 -3
- package/package.json +2 -2
package/dist/browser.js
CHANGED
|
@@ -4117,6 +4117,23 @@ ${allInlineStyles}`.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
|
4117
4117
|
snippet: truncateSnippet(rootTag.raw)
|
|
4118
4118
|
}
|
|
4119
4119
|
];
|
|
4120
|
+
},
|
|
4121
|
+
// subcomposition_linked_style_names_root_id
|
|
4122
|
+
// The compiler flattens a sub-composition root into its host and rewrites inline <style>
|
|
4123
|
+
// selectors to match; a linked stylesheet is not rewritten, so `#root-id` in it matches nothing.
|
|
4124
|
+
({ styles, rootTag, options }) => {
|
|
4125
|
+
if (!options.isSubComposition || !rootTag) return [];
|
|
4126
|
+
const rootId = readAttr(rootTag.raw, "id");
|
|
4127
|
+
if (!rootId) return [];
|
|
4128
|
+
const escaped = rootId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4129
|
+
const names = new RegExp(`#${escaped}(?![\\w-])`);
|
|
4130
|
+
return styles.filter((style) => style.index === -1 && names.test(stripCssComments(style.content))).map((style) => ({
|
|
4131
|
+
code: "subcomposition_linked_style_names_root_id",
|
|
4132
|
+
severity: "warning",
|
|
4133
|
+
message: `The linked stylesheet "${style.rootRelativePath ?? style.file}" has a rule on #${rootId}, the id of this sub-composition's root. The root is merged into its host when the composition is compiled, so that selector matches nothing (the same rule in an inline <style> is rewritten and works).`,
|
|
4134
|
+
fixHint: `Move the #${rootId} rules into a <style> inside the <template>, where they are rewritten for the merged root, or select a child element of the root in the linked file instead of the root itself.`,
|
|
4135
|
+
snippet: truncateSnippet(style.content.slice(0, 120))
|
|
4136
|
+
}));
|
|
4120
4137
|
}
|
|
4121
4138
|
];
|
|
4122
4139
|
var CLIP_MEDIA_TAGS = /* @__PURE__ */ new Set(["img", "video", "audio"]);
|
|
@@ -6872,6 +6889,68 @@ var shaderRules = [
|
|
|
6872
6889
|
})
|
|
6873
6890
|
];
|
|
6874
6891
|
|
|
6892
|
+
// src/rules/rootPaths.ts
|
|
6893
|
+
var URL_ATTRS = ["src", "href", "poster", "xlink:href"];
|
|
6894
|
+
var ROOT_PATH = /^\/(?!\/)/;
|
|
6895
|
+
var CSS_ROOT_URL = /\burl\(\s*["']?(\/(?!\/)[^"')\s]*)/g;
|
|
6896
|
+
var SCRIPT_ROOT_URL = /\b(?:fetch|new\s+URL)\(\s*(["'`])(\/(?!\/)[^"'`$]*)\1/g;
|
|
6897
|
+
function relativeForm(path, cssFile) {
|
|
6898
|
+
const depth = (cssFile ?? "").split("/").filter(Boolean).length - 1;
|
|
6899
|
+
return `${"../".repeat(Math.max(depth, 0))}${path.slice(1)}`;
|
|
6900
|
+
}
|
|
6901
|
+
function rootPathFinding(path, cssFile, snippet) {
|
|
6902
|
+
return {
|
|
6903
|
+
code: "root_path_missing_in_preview",
|
|
6904
|
+
severity: "warning",
|
|
6905
|
+
message: `"${path}" starts at the server's root. Render and snapshot serve the project there, but Studio's preview serves it under the project's own path, so the preview cannot find this file.`,
|
|
6906
|
+
fixHint: `Use "${relativeForm(path, cssFile)}" instead.`,
|
|
6907
|
+
snippet: truncateSnippet(snippet)
|
|
6908
|
+
};
|
|
6909
|
+
}
|
|
6910
|
+
function cssRootPaths(css, cssFile) {
|
|
6911
|
+
return [...css.matchAll(CSS_ROOT_URL)].map(
|
|
6912
|
+
(match) => rootPathFinding(match[1] ?? "", cssFile, match[0])
|
|
6913
|
+
);
|
|
6914
|
+
}
|
|
6915
|
+
function tagRootPaths(tag) {
|
|
6916
|
+
if (tag.name === "a" || tag.name === "base") return [];
|
|
6917
|
+
const paths = URL_ATTRS.map((attr) => readAttr(tag.raw, attr)?.trim() ?? "").filter(
|
|
6918
|
+
(value) => ROOT_PATH.test(value)
|
|
6919
|
+
);
|
|
6920
|
+
return [
|
|
6921
|
+
...paths.map((path) => rootPathFinding(path, void 0, tag.raw)),
|
|
6922
|
+
...cssRootPaths(readAttr(tag.raw, "style") ?? "")
|
|
6923
|
+
];
|
|
6924
|
+
}
|
|
6925
|
+
var scriptRootPaths = (script) => [...script.matchAll(SCRIPT_ROOT_URL)].map(
|
|
6926
|
+
(match) => rootPathFinding(match[2] ?? "", void 0, match[0])
|
|
6927
|
+
);
|
|
6928
|
+
function invalidImportMap(script) {
|
|
6929
|
+
if (!/\btype\s*=\s*["']?importmap\b/i.test(script.attrs)) return [];
|
|
6930
|
+
try {
|
|
6931
|
+
JSON.parse(script.content);
|
|
6932
|
+
return [];
|
|
6933
|
+
} catch (error) {
|
|
6934
|
+
return [
|
|
6935
|
+
{
|
|
6936
|
+
code: "import_map_invalid",
|
|
6937
|
+
severity: "warning",
|
|
6938
|
+
message: `This import map is not valid JSON (${error instanceof Error ? error.message : String(error)}). The browser ignores it, and preview cannot map root module paths ("/assets/x.js") to the project.`,
|
|
6939
|
+
fixHint: "Make the import map valid JSON: double-quoted keys and values, no trailing commas.",
|
|
6940
|
+
snippet: truncateSnippet(script.content)
|
|
6941
|
+
}
|
|
6942
|
+
];
|
|
6943
|
+
}
|
|
6944
|
+
}
|
|
6945
|
+
var rootPathRules = [
|
|
6946
|
+
(ctx) => [
|
|
6947
|
+
...ctx.tags.flatMap(tagRootPaths),
|
|
6948
|
+
...ctx.styles.flatMap((style) => cssRootPaths(style.content, style.rootRelativePath)),
|
|
6949
|
+
...ctx.scripts.flatMap((script) => scriptRootPaths(script.content)),
|
|
6950
|
+
...ctx.scripts.flatMap(invalidImportMap)
|
|
6951
|
+
]
|
|
6952
|
+
];
|
|
6953
|
+
|
|
6875
6954
|
// src/hyperframeLinter.ts
|
|
6876
6955
|
var RULE_GROUPS = [
|
|
6877
6956
|
{ group: "core", rules: coreRules },
|
|
@@ -6884,7 +6963,8 @@ var RULE_GROUPS = [
|
|
|
6884
6963
|
{ group: "fonts", rules: fontRules },
|
|
6885
6964
|
{ group: "slideshow", rules: slideshowRules },
|
|
6886
6965
|
{ group: "structure", rules: structureRules },
|
|
6887
|
-
{ group: "shaders", rules: shaderRules }
|
|
6966
|
+
{ group: "shaders", rules: shaderRules },
|
|
6967
|
+
{ group: "rootPaths", rules: rootPathRules }
|
|
6888
6968
|
];
|
|
6889
6969
|
var LINT_RULE_COUNT = RULE_GROUPS.reduce((n, g) => n + g.rules.length, 0);
|
|
6890
6970
|
var LINT_RULE_GROUP_COUNTS = Object.fromEntries(
|
package/dist/index.js
CHANGED
|
@@ -4123,6 +4123,23 @@ ${allInlineStyles}`.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
|
4123
4123
|
snippet: truncateSnippet(rootTag.raw)
|
|
4124
4124
|
}
|
|
4125
4125
|
];
|
|
4126
|
+
},
|
|
4127
|
+
// subcomposition_linked_style_names_root_id
|
|
4128
|
+
// The compiler flattens a sub-composition root into its host and rewrites inline <style>
|
|
4129
|
+
// selectors to match; a linked stylesheet is not rewritten, so `#root-id` in it matches nothing.
|
|
4130
|
+
({ styles, rootTag, options }) => {
|
|
4131
|
+
if (!options.isSubComposition || !rootTag) return [];
|
|
4132
|
+
const rootId = readAttr(rootTag.raw, "id");
|
|
4133
|
+
if (!rootId) return [];
|
|
4134
|
+
const escaped = rootId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4135
|
+
const names = new RegExp(`#${escaped}(?![\\w-])`);
|
|
4136
|
+
return styles.filter((style) => style.index === -1 && names.test(stripCssComments(style.content))).map((style) => ({
|
|
4137
|
+
code: "subcomposition_linked_style_names_root_id",
|
|
4138
|
+
severity: "warning",
|
|
4139
|
+
message: `The linked stylesheet "${style.rootRelativePath ?? style.file}" has a rule on #${rootId}, the id of this sub-composition's root. The root is merged into its host when the composition is compiled, so that selector matches nothing (the same rule in an inline <style> is rewritten and works).`,
|
|
4140
|
+
fixHint: `Move the #${rootId} rules into a <style> inside the <template>, where they are rewritten for the merged root, or select a child element of the root in the linked file instead of the root itself.`,
|
|
4141
|
+
snippet: truncateSnippet(style.content.slice(0, 120))
|
|
4142
|
+
}));
|
|
4126
4143
|
}
|
|
4127
4144
|
];
|
|
4128
4145
|
var CLIP_MEDIA_TAGS = /* @__PURE__ */ new Set(["img", "video", "audio"]);
|
|
@@ -6878,6 +6895,68 @@ var shaderRules = [
|
|
|
6878
6895
|
})
|
|
6879
6896
|
];
|
|
6880
6897
|
|
|
6898
|
+
// src/rules/rootPaths.ts
|
|
6899
|
+
var URL_ATTRS = ["src", "href", "poster", "xlink:href"];
|
|
6900
|
+
var ROOT_PATH = /^\/(?!\/)/;
|
|
6901
|
+
var CSS_ROOT_URL = /\burl\(\s*["']?(\/(?!\/)[^"')\s]*)/g;
|
|
6902
|
+
var SCRIPT_ROOT_URL = /\b(?:fetch|new\s+URL)\(\s*(["'`])(\/(?!\/)[^"'`$]*)\1/g;
|
|
6903
|
+
function relativeForm(path, cssFile) {
|
|
6904
|
+
const depth = (cssFile ?? "").split("/").filter(Boolean).length - 1;
|
|
6905
|
+
return `${"../".repeat(Math.max(depth, 0))}${path.slice(1)}`;
|
|
6906
|
+
}
|
|
6907
|
+
function rootPathFinding(path, cssFile, snippet) {
|
|
6908
|
+
return {
|
|
6909
|
+
code: "root_path_missing_in_preview",
|
|
6910
|
+
severity: "warning",
|
|
6911
|
+
message: `"${path}" starts at the server's root. Render and snapshot serve the project there, but Studio's preview serves it under the project's own path, so the preview cannot find this file.`,
|
|
6912
|
+
fixHint: `Use "${relativeForm(path, cssFile)}" instead.`,
|
|
6913
|
+
snippet: truncateSnippet(snippet)
|
|
6914
|
+
};
|
|
6915
|
+
}
|
|
6916
|
+
function cssRootPaths(css, cssFile) {
|
|
6917
|
+
return [...css.matchAll(CSS_ROOT_URL)].map(
|
|
6918
|
+
(match) => rootPathFinding(match[1] ?? "", cssFile, match[0])
|
|
6919
|
+
);
|
|
6920
|
+
}
|
|
6921
|
+
function tagRootPaths(tag) {
|
|
6922
|
+
if (tag.name === "a" || tag.name === "base") return [];
|
|
6923
|
+
const paths = URL_ATTRS.map((attr) => readAttr(tag.raw, attr)?.trim() ?? "").filter(
|
|
6924
|
+
(value) => ROOT_PATH.test(value)
|
|
6925
|
+
);
|
|
6926
|
+
return [
|
|
6927
|
+
...paths.map((path) => rootPathFinding(path, void 0, tag.raw)),
|
|
6928
|
+
...cssRootPaths(readAttr(tag.raw, "style") ?? "")
|
|
6929
|
+
];
|
|
6930
|
+
}
|
|
6931
|
+
var scriptRootPaths = (script) => [...script.matchAll(SCRIPT_ROOT_URL)].map(
|
|
6932
|
+
(match) => rootPathFinding(match[2] ?? "", void 0, match[0])
|
|
6933
|
+
);
|
|
6934
|
+
function invalidImportMap(script) {
|
|
6935
|
+
if (!/\btype\s*=\s*["']?importmap\b/i.test(script.attrs)) return [];
|
|
6936
|
+
try {
|
|
6937
|
+
JSON.parse(script.content);
|
|
6938
|
+
return [];
|
|
6939
|
+
} catch (error) {
|
|
6940
|
+
return [
|
|
6941
|
+
{
|
|
6942
|
+
code: "import_map_invalid",
|
|
6943
|
+
severity: "warning",
|
|
6944
|
+
message: `This import map is not valid JSON (${error instanceof Error ? error.message : String(error)}). The browser ignores it, and preview cannot map root module paths ("/assets/x.js") to the project.`,
|
|
6945
|
+
fixHint: "Make the import map valid JSON: double-quoted keys and values, no trailing commas.",
|
|
6946
|
+
snippet: truncateSnippet(script.content)
|
|
6947
|
+
}
|
|
6948
|
+
];
|
|
6949
|
+
}
|
|
6950
|
+
}
|
|
6951
|
+
var rootPathRules = [
|
|
6952
|
+
(ctx) => [
|
|
6953
|
+
...ctx.tags.flatMap(tagRootPaths),
|
|
6954
|
+
...ctx.styles.flatMap((style) => cssRootPaths(style.content, style.rootRelativePath)),
|
|
6955
|
+
...ctx.scripts.flatMap((script) => scriptRootPaths(script.content)),
|
|
6956
|
+
...ctx.scripts.flatMap(invalidImportMap)
|
|
6957
|
+
]
|
|
6958
|
+
];
|
|
6959
|
+
|
|
6881
6960
|
// src/hyperframeLinter.ts
|
|
6882
6961
|
var RULE_GROUPS = [
|
|
6883
6962
|
{ group: "core", rules: coreRules },
|
|
@@ -6890,7 +6969,8 @@ var RULE_GROUPS = [
|
|
|
6890
6969
|
{ group: "fonts", rules: fontRules },
|
|
6891
6970
|
{ group: "slideshow", rules: slideshowRules },
|
|
6892
6971
|
{ group: "structure", rules: structureRules },
|
|
6893
|
-
{ group: "shaders", rules: shaderRules }
|
|
6972
|
+
{ group: "shaders", rules: shaderRules },
|
|
6973
|
+
{ group: "rootPaths", rules: rootPathRules }
|
|
6894
6974
|
];
|
|
6895
6975
|
var LINT_RULE_COUNT = RULE_GROUPS.reduce((n, g) => n + g.rules.length, 0);
|
|
6896
6976
|
var LINT_RULE_GROUP_COUNTS = Object.fromEntries(
|
|
@@ -7066,6 +7146,7 @@ import {
|
|
|
7066
7146
|
resolveLocalAssetCandidates,
|
|
7067
7147
|
resolveProjectRelativeSrc
|
|
7068
7148
|
} from "@hyperframes/parsers/asset-resolution";
|
|
7149
|
+
import { encodeUrlPath } from "@hyperframes/parsers";
|
|
7069
7150
|
|
|
7070
7151
|
// src/hevcPreviewLint.ts
|
|
7071
7152
|
import { execFile } from "child_process";
|
|
@@ -7591,10 +7672,18 @@ function lintProjectAudioFiles(projectDir, htmlSources) {
|
|
|
7591
7672
|
}
|
|
7592
7673
|
return findings;
|
|
7593
7674
|
}
|
|
7675
|
+
function urlSafeSrcOf(projectDir, rootRelative) {
|
|
7676
|
+
if (!/[?#]/.test(rootRelative)) return null;
|
|
7677
|
+
const literal = resolve(projectDir, rootRelative.replace(/^\//, ""));
|
|
7678
|
+
if (!existsSync2(literal)) return null;
|
|
7679
|
+
return encodeUrlPath(rootRelative);
|
|
7680
|
+
}
|
|
7681
|
+
var urlSafeHint = (safe) => `The file exists, but a "#" or "?" in a src ends the file name there. Write the src as "${safe}".`;
|
|
7594
7682
|
function lintAudioSrcNotFound(projectDir, htmlSources) {
|
|
7595
7683
|
const findings = [];
|
|
7596
7684
|
const audioSrcRe = mediaSrcTagRe("audio");
|
|
7597
7685
|
const missingByFile = /* @__PURE__ */ new Map();
|
|
7686
|
+
const urlSafe = /* @__PURE__ */ new Map();
|
|
7598
7687
|
for (const { html, compSrcPath } of htmlSources) {
|
|
7599
7688
|
const scannable = maskNonScannableRanges2(html);
|
|
7600
7689
|
let match;
|
|
@@ -7604,6 +7693,8 @@ function lintAudioSrcNotFound(projectDir, htmlSources) {
|
|
|
7604
7693
|
if (isUnresolvedAssetPlaceholder2(src)) continue;
|
|
7605
7694
|
const rootRelative = compSrcPath ? rewriteAssetPath2(compSrcPath, src, (path) => existsSync2(join3(projectDir, path))) : src;
|
|
7606
7695
|
if (!existsSync2(resolveProjectRelativeSrc(rootRelative, projectDir))) {
|
|
7696
|
+
const safe = urlSafeSrcOf(projectDir, rootRelative);
|
|
7697
|
+
if (safe) urlSafe.set(src, safe);
|
|
7607
7698
|
const file = sourceFile(projectDir, compSrcPath);
|
|
7608
7699
|
missingByFile.set(file, (missingByFile.get(file) ?? /* @__PURE__ */ new Set()).add(src));
|
|
7609
7700
|
}
|
|
@@ -7616,7 +7707,7 @@ function lintAudioSrcNotFound(projectDir, htmlSources) {
|
|
|
7616
7707
|
severity: "error",
|
|
7617
7708
|
file,
|
|
7618
7709
|
message: `<audio> element references file(s) not found in the project: ${unique.join(", ")}. The rendered video will be silent.`,
|
|
7619
|
-
fixHint: unique.length === 1 ? `Add the file "${unique[0]}" to the project directory, or update the src attribute to point to an existing file.` : `Add the missing files to the project directory, or update the src attributes to point to existing files.`
|
|
7710
|
+
fixHint: unique.length === 1 && urlSafe.has(unique[0]) ? urlSafeHint(urlSafe.get(unique[0])) : unique.length === 1 ? `Add the file "${unique[0]}" to the project directory, or update the src attribute to point to an existing file.` : `Add the missing files to the project directory, or update the src attributes to point to existing files.`
|
|
7620
7711
|
});
|
|
7621
7712
|
}
|
|
7622
7713
|
return findings;
|
|
@@ -7625,6 +7716,7 @@ function lintMissingLocalAsset(projectDir, htmlSources) {
|
|
|
7625
7716
|
const findings = [];
|
|
7626
7717
|
const localAssetSrcRe = mediaSrcTagRe("video|img|source");
|
|
7627
7718
|
const missingByTag = /* @__PURE__ */ new Map();
|
|
7719
|
+
const urlSafe = /* @__PURE__ */ new Map();
|
|
7628
7720
|
for (const { html, compSrcPath } of htmlSources) {
|
|
7629
7721
|
const scannable = maskNonScannableRanges2(html);
|
|
7630
7722
|
const re = new RegExp(localAssetSrcRe.source, localAssetSrcRe.flags);
|
|
@@ -7639,6 +7731,12 @@ function lintMissingLocalAsset(projectDir, htmlSources) {
|
|
|
7639
7731
|
const rootRelative = compSrcPath ? rewriteAssetPath2(compSrcPath, src, (path) => existsSync2(join3(projectDir, path))) : src;
|
|
7640
7732
|
const resolvedAsset = resolveExistingLocalAsset3(projectDir, rootRelative);
|
|
7641
7733
|
if (resolvedAsset) continue;
|
|
7734
|
+
const rawRoot = rawSrc.trim();
|
|
7735
|
+
const safe = urlSafeSrcOf(
|
|
7736
|
+
projectDir,
|
|
7737
|
+
compSrcPath ? rewriteAssetPath2(compSrcPath, rawRoot, (path) => existsSync2(join3(projectDir, path))) : rawRoot
|
|
7738
|
+
);
|
|
7739
|
+
if (safe) urlSafe.set(src, safe);
|
|
7642
7740
|
const resolvedKey = resolve(projectDir, rootRelative);
|
|
7643
7741
|
const file = sourceFile(projectDir, compSrcPath);
|
|
7644
7742
|
const bucketKey = `${file}\0${tagName}`;
|
|
@@ -7657,7 +7755,7 @@ function lintMissingLocalAsset(projectDir, htmlSources) {
|
|
|
7657
7755
|
severity: "error",
|
|
7658
7756
|
file,
|
|
7659
7757
|
message: `<${tagName}> element references local file(s) not found in the project: ${unique.join(", ")}. The renderer will silently skip these and produce a video with missing visuals.`,
|
|
7660
|
-
fixHint: unique.length === 1 ? `Add "${unique[0]}" to the project directory, or update the src attribute to point to an existing file. Common cause: captured asset filenames are unreliable (heygen-logo.svg often contains Google, nvidia-logo.svg may contain Autodesk, etc.). Open the contact sheets and verify the file actually exists at this path before referencing it.` : "Add the missing files to the project directory, or update the src attributes to point to existing files. Captured asset filenames are unreliable. Verify against capture/contact-sheets/ and capture/extracted/asset-descriptions.md."
|
|
7758
|
+
fixHint: unique.length === 1 && urlSafe.has(unique[0]) ? urlSafeHint(urlSafe.get(unique[0])) : unique.length === 1 ? `Add "${unique[0]}" to the project directory, or update the src attribute to point to an existing file. Common cause: captured asset filenames are unreliable (heygen-logo.svg often contains Google, nvidia-logo.svg may contain Autodesk, etc.). Open the contact sheets and verify the file actually exists at this path before referencing it.` : "Add the missing files to the project directory, or update the src attributes to point to existing files. Captured asset filenames are unreliable. Verify against capture/contact-sheets/ and capture/extracted/asset-descriptions.md."
|
|
7661
7759
|
});
|
|
7662
7760
|
}
|
|
7663
7761
|
return findings;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/lint",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.208",
|
|
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.208"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "^25.0.10",
|