@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 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 = extractFontFaceFamilies(styles);
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 = extractFontFaceFamilies(styles);
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 = extractFontFaceFamilies(styles);
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 = extractFontFaceFamilies(styles);
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.221",
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.221"
38
+ "@hyperframes/parsers": "0.8.222"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/node": "^25.0.10",