mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 12:54:29 +00:00
fix(cli): decode linked stylesheet paths
This commit is contained in:
@@ -119,6 +119,29 @@ describe("lintProject", () => {
|
||||
expect(finding?.selector).toBe('[data-composition-id="scene"] .title');
|
||||
});
|
||||
|
||||
it("lints percent-encoded linked CSS filenames that exist decoded on disk", () => {
|
||||
const encodedFilename = "%E6%97%A5%E6%9C%AC%E8%AA%9E.css";
|
||||
const project = makeProject(validHtml(), {
|
||||
"scene.html": `<html><head><link rel="stylesheet" href="${encodedFilename}"></head><body>
|
||||
<div id="scene" data-composition-id="scene" data-width="1920" data-height="1080" data-start="0" data-duration="2"></div>
|
||||
<script>window.__timelines = window.__timelines || {}; window.__timelines["scene"] = gsap.timeline({ paused: true });</script>
|
||||
</body></html>`,
|
||||
});
|
||||
writeFileSync(
|
||||
join(project.dir, "compositions", decodeURIComponent(encodedFilename)),
|
||||
'[data-composition-id="scene"] .title { opacity: 0; }',
|
||||
);
|
||||
|
||||
const { results } = lintProject(project);
|
||||
const subResult = results.find((result) => result.file === "compositions/scene.html");
|
||||
const finding = subResult?.result.findings.find(
|
||||
(item) => item.code === "composition_self_attribute_selector",
|
||||
);
|
||||
|
||||
expect(finding).toBeDefined();
|
||||
expect(finding?.selector).toBe('[data-composition-id="scene"] .title');
|
||||
});
|
||||
|
||||
it("aggregates errors across index.html and sub-compositions", () => {
|
||||
const project = makeProject(htmlWithMissingMediaId(), {
|
||||
"overlay.html": htmlWithMissingMediaId(),
|
||||
@@ -528,6 +551,30 @@ describe("texture_mask_asset_not_found", () => {
|
||||
expect(finding).toBeUndefined();
|
||||
});
|
||||
|
||||
it("checks mask-image URLs inside percent-encoded linked CSS filenames", () => {
|
||||
const encodedFilename = "%E6%97%A5%E6%9C%AC%E8%AA%9E.css";
|
||||
const project = makeProject(validHtml(), {
|
||||
"scene.html": `<html><head><link rel="stylesheet" href="${encodedFilename}"></head><body>
|
||||
<div data-composition-id="scene" data-width="1920" data-height="1080">
|
||||
<div class="hf-texture-text hf-texture-lava">TEXT</div>
|
||||
</div>
|
||||
<script>window.__timelines = window.__timelines || {}; window.__timelines["scene"] = gsap.timeline({ paused: true });</script>
|
||||
</body></html>`,
|
||||
});
|
||||
writeFileSync(
|
||||
join(project.dir, "compositions", decodeURIComponent(encodedFilename)),
|
||||
'.hf-texture-lava { mask-image: url("masks/missing.png"); }',
|
||||
);
|
||||
|
||||
const { results } = lintProject(project);
|
||||
const finding = results[0]?.result.findings.find(
|
||||
(item) => item.code === "texture_mask_asset_not_found",
|
||||
);
|
||||
|
||||
expect(finding).toBeDefined();
|
||||
expect(finding?.message).toContain("masks/missing.png");
|
||||
});
|
||||
|
||||
it("resolves root-absolute mask-image URLs from the project root", () => {
|
||||
const html = `<html><body>
|
||||
<div data-composition-id="main" data-width="1920" data-height="1080">
|
||||
|
||||
@@ -62,9 +62,9 @@ function collectExternalStyles(
|
||||
const href = tag.match(/\bhref\s*=\s*["']([^"']+)["']/i)?.[1] ?? "";
|
||||
if (!isLocalStylesheetHref(href)) continue;
|
||||
const rootRelative = compSrcPath ? join(dirname(compSrcPath), href) : href;
|
||||
const resolved = resolve(projectDir, rootRelative);
|
||||
if (!existsSync(resolved)) continue;
|
||||
styles.push({ href, content: readFileSync(resolved, "utf-8") });
|
||||
const stylesheet = resolveExistingLocalAsset(projectDir, rootRelative);
|
||||
if (!stylesheet) continue;
|
||||
styles.push({ href, content: readFileSync(stylesheet.resolved, "utf-8") });
|
||||
}
|
||||
return styles;
|
||||
}
|
||||
@@ -88,9 +88,12 @@ function collectCssSources(projectDir: string, html: string, compSrcPath?: strin
|
||||
if (!isLocalStylesheetHref(href)) continue;
|
||||
|
||||
const rootRelativePath = compSrcPath ? join(dirname(compSrcPath), href) : href;
|
||||
const resolved = resolve(projectDir, rootRelativePath);
|
||||
if (!existsSync(resolved)) continue;
|
||||
sources.push({ content: readFileSync(resolved, "utf-8"), rootRelativePath });
|
||||
const stylesheet = resolveExistingLocalAsset(projectDir, rootRelativePath);
|
||||
if (!stylesheet) continue;
|
||||
sources.push({
|
||||
content: readFileSync(stylesheet.resolved, "utf-8"),
|
||||
rootRelativePath: stylesheet.rootRelativePath,
|
||||
});
|
||||
}
|
||||
|
||||
let tagMatch: RegExpExecArray | null;
|
||||
@@ -146,6 +149,16 @@ function resolveLocalAssetCandidates(projectDir: string, url: string): string[]
|
||||
return candidates;
|
||||
}
|
||||
|
||||
function resolveExistingLocalAsset(
|
||||
projectDir: string,
|
||||
url: string,
|
||||
): { resolved: string; rootRelativePath: string } | null {
|
||||
const projectRoot = resolve(projectDir);
|
||||
const resolved = resolveLocalAssetCandidates(projectRoot, url).find(existsSync);
|
||||
if (!resolved) return null;
|
||||
return { resolved, rootRelativePath: relative(projectRoot, resolved) };
|
||||
}
|
||||
|
||||
function resolveCssAssetCandidates(
|
||||
projectDir: string,
|
||||
url: string,
|
||||
|
||||
Reference in New Issue
Block a user