@hyperframes/lint 0.8.221 → 0.8.222
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.d.ts +1 -0
- package/dist/browser.js +5 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +105 -4
- package/package.json +2 -2
package/dist/browser.d.ts
CHANGED
|
@@ -51,6 +51,7 @@ type HyperframeLinterOptions = {
|
|
|
51
51
|
* render time — the font will silently fall back to whatever the OS provides.
|
|
52
52
|
*/
|
|
53
53
|
distributed?: boolean;
|
|
54
|
+
projectFontFamilies?: string[];
|
|
54
55
|
/** Who is running the lint: Studio raises the structure rules to errors, the CLI keeps them warnings. */
|
|
55
56
|
host?: "studio" | "cli";
|
|
56
57
|
};
|
package/dist/browser.js
CHANGED
|
@@ -6439,6 +6439,9 @@ function extractFontFaceFamilies(styles) {
|
|
|
6439
6439
|
}
|
|
6440
6440
|
return families;
|
|
6441
6441
|
}
|
|
6442
|
+
function declaredFontFamilies(styles, options) {
|
|
6443
|
+
return /* @__PURE__ */ new Set([...extractFontFaceFamilies(styles), ...options.projectFontFamilies ?? []]);
|
|
6444
|
+
}
|
|
6442
6445
|
function normalizeUsedFontName(part) {
|
|
6443
6446
|
const name = part.trim().replace(/\s*!important\s*$/i, "").replace(/^['"]|['"]$/g, "").trim().toLowerCase();
|
|
6444
6447
|
if (!name || name.includes("(") || name.includes(")")) return null;
|
|
@@ -6548,7 +6551,7 @@ var fontRules = [
|
|
|
6548
6551
|
// so there is nothing for the author to act on.
|
|
6549
6552
|
({ styles, options }) => {
|
|
6550
6553
|
if (!options.distributed) return [];
|
|
6551
|
-
const declared =
|
|
6554
|
+
const declared = declaredFontFamilies(styles, options);
|
|
6552
6555
|
const used = extractUsedFontFamilies(styles);
|
|
6553
6556
|
const aliased = collectAliasedFonts(used, declared);
|
|
6554
6557
|
if (aliased.length === 0) return [];
|
|
@@ -6564,7 +6567,7 @@ var fontRules = [
|
|
|
6564
6567
|
({ styles, source, rawSource, options, locate }) => {
|
|
6565
6568
|
if (isRegistrySourceFile(options.filePath) || isRegistryInstalledFile(rawSource)) return [];
|
|
6566
6569
|
const findings = [];
|
|
6567
|
-
const declared =
|
|
6570
|
+
const declared = declaredFontFamilies(styles, options);
|
|
6568
6571
|
const used = extractUsedFontFamilies(styles);
|
|
6569
6572
|
const googleFonts = collectGoogleFontFamilies(source, styles);
|
|
6570
6573
|
const undeclared = used.filter(
|
package/dist/index.d.ts
CHANGED
|
@@ -51,6 +51,7 @@ type HyperframeLinterOptions = {
|
|
|
51
51
|
* render time — the font will silently fall back to whatever the OS provides.
|
|
52
52
|
*/
|
|
53
53
|
distributed?: boolean;
|
|
54
|
+
projectFontFamilies?: string[];
|
|
54
55
|
/** Who is running the lint: Studio raises the structure rules to errors, the CLI keeps them warnings. */
|
|
55
56
|
host?: "studio" | "cli";
|
|
56
57
|
};
|
package/dist/index.js
CHANGED
|
@@ -6445,6 +6445,9 @@ function extractFontFaceFamilies(styles) {
|
|
|
6445
6445
|
}
|
|
6446
6446
|
return families;
|
|
6447
6447
|
}
|
|
6448
|
+
function declaredFontFamilies(styles, options) {
|
|
6449
|
+
return /* @__PURE__ */ new Set([...extractFontFaceFamilies(styles), ...options.projectFontFamilies ?? []]);
|
|
6450
|
+
}
|
|
6448
6451
|
function normalizeUsedFontName(part) {
|
|
6449
6452
|
const name = part.trim().replace(/\s*!important\s*$/i, "").replace(/^['"]|['"]$/g, "").trim().toLowerCase();
|
|
6450
6453
|
if (!name || name.includes("(") || name.includes(")")) return null;
|
|
@@ -6554,7 +6557,7 @@ var fontRules = [
|
|
|
6554
6557
|
// so there is nothing for the author to act on.
|
|
6555
6558
|
({ styles, options }) => {
|
|
6556
6559
|
if (!options.distributed) return [];
|
|
6557
|
-
const declared =
|
|
6560
|
+
const declared = declaredFontFamilies(styles, options);
|
|
6558
6561
|
const used = extractUsedFontFamilies(styles);
|
|
6559
6562
|
const aliased = collectAliasedFonts(used, declared);
|
|
6560
6563
|
if (aliased.length === 0) return [];
|
|
@@ -6570,7 +6573,7 @@ var fontRules = [
|
|
|
6570
6573
|
({ styles, source, rawSource, options, locate }) => {
|
|
6571
6574
|
if (isRegistrySourceFile(options.filePath) || isRegistryInstalledFile(rawSource)) return [];
|
|
6572
6575
|
const findings = [];
|
|
6573
|
-
const declared =
|
|
6576
|
+
const declared = declaredFontFamilies(styles, options);
|
|
6574
6577
|
const used = extractUsedFontFamilies(styles);
|
|
6575
6578
|
const googleFonts = collectGoogleFontFamilies(source, styles);
|
|
6576
6579
|
const undeclared = used.filter(
|
|
@@ -7522,6 +7525,42 @@ function resolveCssAssetCandidates(projectDir, url, htmlCompSrcPath, cssRootRela
|
|
|
7522
7525
|
}
|
|
7523
7526
|
return resolveLocalAssetCandidates(projectDir, url);
|
|
7524
7527
|
}
|
|
7528
|
+
function mountedSources(projectDir, rootHtml, rootCompSrcPath) {
|
|
7529
|
+
const sources = [{ html: rootHtml, compSrcPath: rootCompSrcPath }];
|
|
7530
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7531
|
+
for (const source of sources) {
|
|
7532
|
+
const { document } = parseHTML3(source.html);
|
|
7533
|
+
for (const host of querySelectorAllIncludingTemplates(document, "[data-composition-src]")) {
|
|
7534
|
+
const src = host.getAttribute("data-composition-src")?.trim();
|
|
7535
|
+
if (!src || isUnresolvedAssetPlaceholder2(src) || isRemoteOrInlineUrl2(src)) continue;
|
|
7536
|
+
const path = resolve(projectDir, src);
|
|
7537
|
+
if (seen.has(path) || !isWithinProjectRoot2(projectDir, path)) continue;
|
|
7538
|
+
seen.add(path);
|
|
7539
|
+
const read = readProjectFile2(path);
|
|
7540
|
+
if (read.kind !== "file") continue;
|
|
7541
|
+
sources.push({
|
|
7542
|
+
html: read.text,
|
|
7543
|
+
compSrcPath: relative2(resolve(projectDir), path).replace(/\\/g, "/")
|
|
7544
|
+
});
|
|
7545
|
+
}
|
|
7546
|
+
}
|
|
7547
|
+
return sources;
|
|
7548
|
+
}
|
|
7549
|
+
function declaredProjectFontFamilies(projectDir, sources) {
|
|
7550
|
+
const families = /* @__PURE__ */ new Set();
|
|
7551
|
+
for (const { html, compSrcPath } of sources) {
|
|
7552
|
+
const { document } = parseHTML3(html);
|
|
7553
|
+
const styles = [
|
|
7554
|
+
...querySelectorAllIncludingTemplates(document, "style").map((style) => ({
|
|
7555
|
+
content: style.textContent ?? ""
|
|
7556
|
+
})),
|
|
7557
|
+
...collectLocalStylesheets(projectDir, document, compSrcPath)
|
|
7558
|
+
];
|
|
7559
|
+
for (const family of extractFontFaceFamilies(styles)) families.add(family);
|
|
7560
|
+
for (const family of collectGoogleFontFamilies(html, styles)) families.add(family);
|
|
7561
|
+
}
|
|
7562
|
+
return [...families];
|
|
7563
|
+
}
|
|
7525
7564
|
async function lintProject(projectDir, entryFile, hostOptions = {}) {
|
|
7526
7565
|
const indexPath = entryFile ? resolve(entryFile) : resolve(projectDir, "index.html");
|
|
7527
7566
|
if (entryFile && !isWithinProjectRoot2(projectDir, indexPath)) {
|
|
@@ -7534,11 +7573,16 @@ async function lintProject(projectDir, entryFile, hostOptions = {}) {
|
|
|
7534
7573
|
let totalWarnings = 0;
|
|
7535
7574
|
let totalInfos = 0;
|
|
7536
7575
|
const rootHtml = readFileSync(indexPath, "utf-8");
|
|
7576
|
+
const projectFontFamilies = declaredProjectFontFamilies(
|
|
7577
|
+
projectDir,
|
|
7578
|
+
mountedSources(projectDir, rootHtml, rootCompSrcPath)
|
|
7579
|
+
);
|
|
7537
7580
|
const rootResult = await lintHyperframeHtml(rootHtml, {
|
|
7538
7581
|
...hostOptions,
|
|
7539
7582
|
filePath: indexPath,
|
|
7540
7583
|
compSrcPath: rootFile,
|
|
7541
|
-
externalStyles: collectExternalStyles(projectDir, rootHtml, rootCompSrcPath)
|
|
7584
|
+
externalStyles: collectExternalStyles(projectDir, rootHtml, rootCompSrcPath),
|
|
7585
|
+
projectFontFamilies
|
|
7542
7586
|
});
|
|
7543
7587
|
results.push({ file: rootFile, result: rootResult, contentHash: contentDigest(rootHtml) });
|
|
7544
7588
|
totalErrors += rootResult.errorCount;
|
|
@@ -7572,7 +7616,8 @@ async function lintProject(projectDir, entryFile, hostOptions = {}) {
|
|
|
7572
7616
|
filePath,
|
|
7573
7617
|
isSubComposition: true,
|
|
7574
7618
|
compSrcPath,
|
|
7575
|
-
externalStyles: collectExternalStyles(projectDir, html, compSrcPath)
|
|
7619
|
+
externalStyles: collectExternalStyles(projectDir, html, compSrcPath),
|
|
7620
|
+
projectFontFamilies
|
|
7576
7621
|
});
|
|
7577
7622
|
results.push({
|
|
7578
7623
|
file: `compositions/${file}`,
|
|
@@ -7590,6 +7635,7 @@ async function lintProject(projectDir, entryFile, hostOptions = {}) {
|
|
|
7590
7635
|
...lintAudioSrcNotFound(projectDir, allHtmlSources),
|
|
7591
7636
|
...lintMissingLocalAsset(projectDir, allHtmlSources),
|
|
7592
7637
|
...lintTextureMaskAssetNotFound(projectDir, allHtmlSources),
|
|
7638
|
+
...lintLinkedStylesheetAssets(projectDir, allHtmlSources),
|
|
7593
7639
|
...!entryFile ? lintMultipleRootCompositions(projectDir) : [],
|
|
7594
7640
|
...!entryFile ? lintBlankRootWithStandaloneComposition(rootHtml, allHtmlSources) : [],
|
|
7595
7641
|
...lintDuplicateAudioTracks(projectDir, rootHtml),
|
|
@@ -7799,6 +7845,61 @@ function lintTextureMaskAssetNotFound(projectDir, htmlSources) {
|
|
|
7799
7845
|
};
|
|
7800
7846
|
});
|
|
7801
7847
|
}
|
|
7848
|
+
function nearestAncestorMatch(projectDir, cssRootRelativePath, url) {
|
|
7849
|
+
const root = resolve(projectDir);
|
|
7850
|
+
for (let dir = resolve(root, dirname2(cssRootRelativePath), ".."); ; dir = dirname2(dir)) {
|
|
7851
|
+
if (!isWithinProjectRoot2(projectDir, dir)) return null;
|
|
7852
|
+
if (resolveLocalAssetCandidates(projectDir, relative2(root, join3(dir, url))).some(existsSync2)) {
|
|
7853
|
+
return relative2(dirname2(resolve(root, cssRootRelativePath)), join3(dir, url)).replace(
|
|
7854
|
+
/\\/g,
|
|
7855
|
+
"/"
|
|
7856
|
+
);
|
|
7857
|
+
}
|
|
7858
|
+
if (dir === root) return null;
|
|
7859
|
+
}
|
|
7860
|
+
}
|
|
7861
|
+
function lintLinkedStylesheetAssets(projectDir, htmlSources) {
|
|
7862
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7863
|
+
const findings = [];
|
|
7864
|
+
for (const { html, compSrcPath } of htmlSources) {
|
|
7865
|
+
for (const { content, rootRelativePath } of collectLocalStylesheets(
|
|
7866
|
+
projectDir,
|
|
7867
|
+
parseHTML3(html).document,
|
|
7868
|
+
compSrcPath
|
|
7869
|
+
)) {
|
|
7870
|
+
let root;
|
|
7871
|
+
try {
|
|
7872
|
+
root = postcss4.parse(content);
|
|
7873
|
+
} catch (error) {
|
|
7874
|
+
if (!(error instanceof postcss4.CssSyntaxError)) throw error;
|
|
7875
|
+
continue;
|
|
7876
|
+
}
|
|
7877
|
+
root.walkDecls((declaration) => {
|
|
7878
|
+
if (/^(?:-webkit-)?mask-image$/i.test(declaration.prop)) return;
|
|
7879
|
+
for (const match of declaration.value.matchAll(CSS_URL_RE)) {
|
|
7880
|
+
const rawUrl = match[1] ?? match[2] ?? match[3] ?? "";
|
|
7881
|
+
if (isUnresolvedAssetPlaceholder2(rawUrl)) continue;
|
|
7882
|
+
const url = cleanAssetUrl2(rawUrl);
|
|
7883
|
+
if (!url || isRemoteOrInlineUrl2(url) || url.startsWith("/")) continue;
|
|
7884
|
+
if (resolveCssAssetCandidates(projectDir, url, void 0, rootRelativePath).some(existsSync2))
|
|
7885
|
+
continue;
|
|
7886
|
+
const key = `${rootRelativePath}\0${url}`;
|
|
7887
|
+
if (seen.has(key)) continue;
|
|
7888
|
+
seen.add(key);
|
|
7889
|
+
const found = nearestAncestorMatch(projectDir, rootRelativePath, url);
|
|
7890
|
+
findings.push({
|
|
7891
|
+
code: "missing_stylesheet_asset",
|
|
7892
|
+
severity: "warning",
|
|
7893
|
+
file: join3(projectDir, rootRelativePath),
|
|
7894
|
+
message: `${rootRelativePath} references url(${url}), which is not found beside the stylesheet (looked in ${dirname2(rootRelativePath)}/). The browser resolves url() from the stylesheet, so the file will 404.`,
|
|
7895
|
+
fixHint: found ? `A file exists at "${found}" relative to the stylesheet. Change the url() to that path.` : `Add "${url}" beside the stylesheet, or point the url() at an existing file.`
|
|
7896
|
+
});
|
|
7897
|
+
}
|
|
7898
|
+
});
|
|
7899
|
+
}
|
|
7900
|
+
}
|
|
7901
|
+
return findings;
|
|
7902
|
+
}
|
|
7802
7903
|
function lintMultipleRootCompositions(projectDir) {
|
|
7803
7904
|
const findings = [];
|
|
7804
7905
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/lint",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.222",
|
|
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.222"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "^25.0.10",
|