@hyperframes/lint 0.9.19 → 0.9.21

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 CHANGED
@@ -1679,6 +1679,11 @@ var coreRules = [
1679
1679
  label: "crypto.getRandomValues()",
1680
1680
  hint: "Use a seeded PRNG (e.g. a simple mulberry32) so renders are deterministic across frames."
1681
1681
  },
1682
+ {
1683
+ pattern: /crypto\.randomUUID\s*\(/,
1684
+ label: "crypto.randomUUID()",
1685
+ hint: "Use a seeded PRNG (e.g. a simple mulberry32) so renders are deterministic across frames."
1686
+ },
1682
1687
  {
1683
1688
  pattern: /gsap\.utils\.random\s*\(/,
1684
1689
  label: "gsap.utils.random()",
package/dist/index.d.ts CHANGED
@@ -107,9 +107,15 @@ interface ProjectLintResult {
107
107
  totalErrors: number;
108
108
  totalWarnings: number;
109
109
  totalInfos: number;
110
+ unmountedCompositions?: string[];
111
+ }
112
+ /** The one line a run prints when it left unmounted compositions out. */
113
+ declare function unmountedNotice(count: number): string;
114
+ interface ProjectLintOptions {
115
+ includeUnmounted?: boolean;
110
116
  }
111
117
  declare function linkedSources(projectDir: string, html: string, compSrcPath?: string): Pick<HyperframeLinterOptions, "externalStyles" | "externalScripts">;
112
- declare function lintProject(projectDir: string, entryFile?: string, hostOptions?: Pick<HyperframeLinterOptions, "host" | "isSubComposition">): Promise<ProjectLintResult>;
118
+ declare function lintProject(projectDir: string, entryFile?: string, hostOptions?: Pick<HyperframeLinterOptions, "host" | "isSubComposition">, { includeUnmounted }?: ProjectLintOptions): Promise<ProjectLintResult>;
113
119
 
114
120
  /** What the project leaves out; ask with a relative `/` path, a folder with a trailing `/`. */
115
121
  declare function projectIgnore(projectDir: string): Ignore;
@@ -121,4 +127,4 @@ type AudioAttribute = "data-automation" | "data-fx-chain";
121
127
  */
122
128
  declare function audioAttributeShapeError(attr: AudioAttribute, json: string): string | null;
123
129
 
124
- export { type AudioAttribute, type HyperframeLintFinding, type HyperframeLintResult, type HyperframeLintSeverity, type HyperframeLinterOptions, LINT_RULE_COUNT, LINT_RULE_GROUP_COUNTS, type LintTimings, type ProjectLintResult, audioAttributeShapeError, linkedSources, lintHyperframeHtml, lintMediaUrls, lintProject, projectIgnore, shouldBlockRender };
130
+ export { type AudioAttribute, type HyperframeLintFinding, type HyperframeLintResult, type HyperframeLintSeverity, type HyperframeLinterOptions, LINT_RULE_COUNT, LINT_RULE_GROUP_COUNTS, type LintTimings, type ProjectLintOptions, type ProjectLintResult, audioAttributeShapeError, linkedSources, lintHyperframeHtml, lintMediaUrls, lintProject, projectIgnore, shouldBlockRender, unmountedNotice };
package/dist/index.js CHANGED
@@ -1685,6 +1685,11 @@ var coreRules = [
1685
1685
  label: "crypto.getRandomValues()",
1686
1686
  hint: "Use a seeded PRNG (e.g. a simple mulberry32) so renders are deterministic across frames."
1687
1687
  },
1688
+ {
1689
+ pattern: /crypto\.randomUUID\s*\(/,
1690
+ label: "crypto.randomUUID()",
1691
+ hint: "Use a seeded PRNG (e.g. a simple mulberry32) so renders are deterministic across frames."
1692
+ },
1688
1693
  {
1689
1694
  pattern: /gsap\.utils\.random\s*\(/,
1690
1695
  label: "gsap.utils.random()",
@@ -7611,6 +7616,10 @@ function querySelectorAllIncludingTemplates(root, selector) {
7611
7616
  }
7612
7617
  return matches;
7613
7618
  }
7619
+ function unmountedNotice(count) {
7620
+ const skipped = count === 1 ? "1 file in compositions/ is" : `${count} files in compositions/ are`;
7621
+ return `${skipped} not mounted from index.html, so not checked; --include-unmounted adds them.`;
7622
+ }
7614
7623
  function contentDigest(html) {
7615
7624
  return createHash("sha256").update(html).digest("hex").slice(0, 16);
7616
7625
  }
@@ -7750,7 +7759,7 @@ function declaredProjectFontFamilies(projectDir, sources) {
7750
7759
  }
7751
7760
  return [...families];
7752
7761
  }
7753
- async function lintProject(projectDir, entryFile, hostOptions = {}) {
7762
+ async function lintProject(projectDir, entryFile, hostOptions = {}, { includeUnmounted = false } = {}) {
7754
7763
  const indexPath = entryFile ? resolve(entryFile) : resolve(projectDir, "index.html");
7755
7764
  if (entryFile && !isWithinProjectRoot2(projectDir, indexPath)) {
7756
7765
  throw new Error(`Explicit lint entry is outside the project directory: ${entryFile}`);
@@ -7762,10 +7771,11 @@ async function lintProject(projectDir, entryFile, hostOptions = {}) {
7762
7771
  let totalWarnings = 0;
7763
7772
  let totalInfos = 0;
7764
7773
  const rootHtml = readTextFile(indexPath);
7765
- const projectFontFamilies = declaredProjectFontFamilies(
7766
- projectDir,
7767
- mountedSources(projectDir, rootHtml, rootCompSrcPath)
7768
- );
7774
+ const mounted = mountedSources(projectDir, rootHtml, rootCompSrcPath);
7775
+ const mountedPaths = new Set(mounted.map((source) => source.compSrcPath));
7776
+ const unmountedCompositions = [];
7777
+ const unmountedSources = [];
7778
+ const projectFontFamilies = declaredProjectFontFamilies(projectDir, mounted);
7769
7779
  const rootResult = await lintHyperframeHtml(rootHtml, {
7770
7780
  ...hostOptions,
7771
7781
  filePath: indexPath,
@@ -7800,6 +7810,11 @@ async function lintProject(projectDir, entryFile, hostOptions = {}) {
7800
7810
  const filePath = join4(compositionsDir, file);
7801
7811
  const html = readTextFile(filePath);
7802
7812
  const compSrcPath = `compositions/${file}`;
7813
+ if (!includeUnmounted && !mountedPaths.has(compSrcPath)) {
7814
+ unmountedSources.push({ html, compSrcPath });
7815
+ if (!isSnippetFragment(html)) unmountedCompositions.push(compSrcPath);
7816
+ continue;
7817
+ }
7803
7818
  allHtmlSources.push({ html, compSrcPath });
7804
7819
  if (isSnippetFragment(html)) continue;
7805
7820
  const result = await lintHyperframeHtml(html, {
@@ -7828,7 +7843,7 @@ async function lintProject(projectDir, entryFile, hostOptions = {}) {
7828
7843
  ...lintTextureMaskAssetNotFound(projectDir, allHtmlSources),
7829
7844
  ...lintLinkedStylesheetAssets(projectDir, allHtmlSources),
7830
7845
  ...!entryFile ? lintMultipleRootCompositions(projectDir) : [],
7831
- ...!entryFile ? lintBlankRootWithStandaloneComposition(rootHtml, allHtmlSources) : [],
7846
+ ...!entryFile ? lintBlankRootWithStandaloneComposition(rootHtml, [...allHtmlSources, ...unmountedSources]) : [],
7832
7847
  ...lintDuplicateAudioTracks(projectDir, rootHtml),
7833
7848
  ...lintMissingOrEmptySubComposition(projectDir, rootHtml),
7834
7849
  ...await lintVideoMediaStartPastEof(projectDir, allHtmlSources),
@@ -7850,7 +7865,7 @@ async function lintProject(projectDir, entryFile, hostOptions = {}) {
7850
7865
  totalInfos++;
7851
7866
  }
7852
7867
  }
7853
- return { results, totalErrors, totalWarnings, totalInfos };
7868
+ return { results, totalErrors, totalWarnings, totalInfos, unmountedCompositions };
7854
7869
  }
7855
7870
  function lintBlankRootWithStandaloneComposition(rootHtml, htmlSources) {
7856
7871
  const { document: rootDocument } = parseHTML3(rootHtml);
@@ -7995,6 +8010,12 @@ function lintMissingLocalAsset(projectDir, htmlSources) {
7995
8010
  }
7996
8011
  return findings;
7997
8012
  }
8013
+ function localCssUrl(match) {
8014
+ const rawUrl = match[1] ?? match[2] ?? match[3] ?? "";
8015
+ if (isUnresolvedAssetPlaceholder2(rawUrl)) return void 0;
8016
+ const url = cleanAssetUrl2(rawUrl);
8017
+ return url && !isRemoteOrInlineUrl2(url) ? url : void 0;
8018
+ }
7998
8019
  function lintTextureMaskAssetNotFound(projectDir, htmlSources) {
7999
8020
  const missingByFile = /* @__PURE__ */ new Map();
8000
8021
  for (const { html, compSrcPath } of htmlSources) {
@@ -8010,10 +8031,8 @@ function lintTextureMaskAssetNotFound(projectDir, htmlSources) {
8010
8031
  matches = [...cssSource.content.matchAll(MASK_IMAGE_URL_RE)];
8011
8032
  }
8012
8033
  for (const match of matches) {
8013
- const rawUrl = match[1] ?? match[2] ?? match[3] ?? "";
8014
- if (isUnresolvedAssetPlaceholder2(rawUrl)) continue;
8015
- const url = cleanAssetUrl2(rawUrl);
8016
- if (!url || isRemoteOrInlineUrl2(url)) continue;
8034
+ const url = localCssUrl(match);
8035
+ if (!url) continue;
8017
8036
  const candidates = resolveCssAssetCandidates(
8018
8037
  projectDir,
8019
8038
  url,
@@ -8068,10 +8087,8 @@ function lintLinkedStylesheetAssets(projectDir, htmlSources) {
8068
8087
  root.walkDecls((declaration) => {
8069
8088
  if (/^(?:-webkit-)?mask-image$/i.test(declaration.prop)) return;
8070
8089
  for (const match of declaration.value.matchAll(CSS_URL_RE)) {
8071
- const rawUrl = match[1] ?? match[2] ?? match[3] ?? "";
8072
- if (isUnresolvedAssetPlaceholder2(rawUrl)) continue;
8073
- const url = cleanAssetUrl2(rawUrl);
8074
- if (!url || isRemoteOrInlineUrl2(url) || url.startsWith("/")) continue;
8090
+ const url = localCssUrl(match);
8091
+ if (!url || url.startsWith("/")) continue;
8075
8092
  if (resolveCssAssetCandidates(projectDir, url, void 0, rootRelativePath).some(existsSync3))
8076
8093
  continue;
8077
8094
  const key = `${rootRelativePath}\0${url}`;
@@ -8271,5 +8288,6 @@ export {
8271
8288
  lintMediaUrls,
8272
8289
  lintProject,
8273
8290
  projectIgnore,
8274
- shouldBlockRender
8291
+ shouldBlockRender,
8292
+ unmountedNotice
8275
8293
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/lint",
3
- "version": "0.9.19",
3
+ "version": "0.9.21",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/heygen-com/hyperframes",
@@ -36,7 +36,7 @@
36
36
  "linkedom": "^0.18.12",
37
37
  "postcss": "^8.5.8",
38
38
  "postcss-selector-parser": "^7.1.4",
39
- "@hyperframes/parsers": "0.9.19"
39
+ "@hyperframes/parsers": "0.9.21"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/node": "^25.0.10",