@hyperframes/lint 0.8.191 → 0.8.192

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
@@ -2618,7 +2618,11 @@ function containingCallOffset(script, tokenOffset) {
2618
2618
  }
2619
2619
 
2620
2620
  // src/rules/composition.ts
2621
- import { COMPOSITION_VARIABLE_TYPES, isSafeMediaUrl } from "@hyperframes/parsers/composition";
2621
+ import {
2622
+ COMPOSITION_VARIABLE_TYPES,
2623
+ isCompositionVariable,
2624
+ isSafeMediaUrl
2625
+ } from "@hyperframes/parsers/composition";
2622
2626
  import { COMPOSITION_ATTRIBUTES, readClipTiming } from "@hyperframes/parsers/composition-contract";
2623
2627
  import { resolveCompositionDuration } from "@hyperframes/parsers/composition-duration";
2624
2628
  import {
@@ -2922,6 +2926,16 @@ function variablesDeclarationFindings(tag, tags) {
2922
2926
  });
2923
2927
  continue;
2924
2928
  }
2929
+ if (!isCompositionVariable(e)) {
2930
+ findings.push({
2931
+ code: "invalid_composition_variables_declaration",
2932
+ severity: "error",
2933
+ message: `data-composition-variables entry [${i}] has a default that does not match its type, or an enum without an options array.`,
2934
+ fixHint: "Use number defaults for number variables, boolean defaults for boolean variables, and string defaults for other types. Enum variables also require an options array.",
2935
+ snippet: truncateSnippet(tag.raw)
2936
+ });
2937
+ continue;
2938
+ }
2925
2939
  const id = String(e.id);
2926
2940
  if ((e.type === "image" || varSrcIds.has(id)) && typeof e.default === "string" && e.default.length > 0 && !isSafeMediaUrl(e.default)) {
2927
2941
  findings.push({
@@ -4960,7 +4974,7 @@ ${right.raw}`)
4960
4974
  return findings;
4961
4975
  },
4962
4976
  // gsap_timeline_registered_before_async_build — registering window.__timelines[id]
4963
- // BEFORE the timeline is built inside document.fonts.ready (or any async callback)
4977
+ // BEFORE the timeline is built inside a font-ready callback
4964
4978
  // leaves an EMPTY timeline registered. The runtime's sub-composition readiness gate
4965
4979
  // treats "key present" as "ready" and nests the child ONCE, while still empty — so the
4966
4980
  // animation never renders when this composition is mounted as a sub-composition.
@@ -4969,9 +4983,11 @@ ${right.raw}`)
4969
4983
  const findings = [];
4970
4984
  for (const script of scripts) {
4971
4985
  const content = stripJsComments(script.content);
4972
- const regIdx = content.search(/window\s*\.\s*__timelines\s*\[/);
4986
+ const regIdx = content.search(
4987
+ /window\s*\.\s*__timelines\s*(?:\[[^\]]+\]|\.\s*[A-Za-z_$][\w$]*)\s*=\s*(?!=)/
4988
+ );
4973
4989
  if (regIdx < 0) continue;
4974
- const fontsReadyIdx = content.search(/document\s*\.\s*fonts\s*\.\s*ready/);
4990
+ const fontsReadyIdx = content.search(/document\s*\.\s*fonts\s*\.\s*(?:ready\b|load\s*\()/);
4975
4991
  if (fontsReadyIdx < 0) continue;
4976
4992
  if (regIdx >= fontsReadyIdx) continue;
4977
4993
  const tail = content.slice(fontsReadyIdx);
@@ -4979,8 +4995,8 @@ ${right.raw}`)
4979
4995
  findings.push({
4980
4996
  code: "gsap_timeline_registered_before_async_build",
4981
4997
  severity: "error",
4982
- message: 'window.__timelines is assigned BEFORE the timeline is built inside document.fonts.ready. An empty timeline registered early gets nested empty when this composition is used as a sub-composition (the readiness gate treats "key present" as "ready" and never re-nests), so the animation renders blank.',
4983
- fixHint: "Move the `window.__timelines[id] = tl;` assignment to the END of the document.fonts.ready callback, after the tweens are added. Optionally call window.__hfForceTimelineRebind() right after, to re-nest the populated timeline."
4998
+ message: 'window.__timelines is assigned BEFORE the timeline is built inside document.fonts.ready or document.fonts.load(). An empty timeline registered early gets nested empty when this composition is used as a sub-composition (the readiness gate treats "key present" as "ready" and never re-nests), so the animation renders blank.',
4999
+ fixHint: "Move the `window.__timelines[id] = tl;` assignment to the END of the font-ready callback, after the tweens are added. Optionally call window.__hfForceTimelineRebind() right after, to re-nest the populated timeline."
4984
5000
  });
4985
5001
  }
4986
5002
  return findings;
@@ -6020,22 +6036,29 @@ var GENERIC_FAMILIES = /* @__PURE__ */ new Set([
6020
6036
  "unset",
6021
6037
  "revert"
6022
6038
  ]);
6023
- function stripCssComments2(css) {
6024
- return css.replace(/\/\*[\s\S]*?\*\//g, " ");
6039
+ function parseFontCss(css) {
6040
+ try {
6041
+ return postcss3.parse(css);
6042
+ } catch {
6043
+ return null;
6044
+ }
6045
+ }
6046
+ function isFontFaceDeclaration(decl) {
6047
+ let parent = decl.parent;
6048
+ while (parent) {
6049
+ if (parent.type === "atrule" && parent.name.toLowerCase() === "font-face") return true;
6050
+ parent = parent.parent;
6051
+ }
6052
+ return false;
6025
6053
  }
6026
6054
  function extractFontFaceFamilies(styles) {
6027
6055
  const families = /* @__PURE__ */ new Set();
6028
- const fontFaceRe = /@font-face\s*\{[^}]*\}/gi;
6029
- const familyRe = /font-family\s*:\s*(['"]?)([^;'"]+)\1/i;
6030
6056
  for (const style of styles) {
6031
- const content = stripCssComments2(style.content);
6032
- let match;
6033
- while ((match = fontFaceRe.exec(content)) !== null) {
6034
- const familyMatch = match[0].match(familyRe);
6035
- if (familyMatch?.[2]) {
6036
- families.add(familyMatch[2].trim().toLowerCase());
6037
- }
6038
- }
6057
+ parseFontCss(style.content)?.walkDecls(/^font-family$/i, (decl) => {
6058
+ if (!isFontFaceDeclaration(decl)) return;
6059
+ const name = normalizeUsedFontName(decl.value);
6060
+ if (name) families.add(name);
6061
+ });
6039
6062
  }
6040
6063
  return families;
6041
6064
  }
@@ -6044,22 +6067,43 @@ function normalizeUsedFontName(part) {
6044
6067
  if (!name || name.includes("(") || name.includes(")")) return null;
6045
6068
  return name;
6046
6069
  }
6070
+ function collectFontCustomProperties(styles) {
6071
+ const properties = /* @__PURE__ */ new Map();
6072
+ for (const style of styles) {
6073
+ parseFontCss(style.content)?.walkDecls(/^--/, (decl) => {
6074
+ if (!isFontFaceDeclaration(decl)) properties.set(decl.prop, decl.value);
6075
+ });
6076
+ }
6077
+ return properties;
6078
+ }
6079
+ function resolveUsedFontNames(value, properties, resolving = /* @__PURE__ */ new Set()) {
6080
+ return postcss3.list.comma(value).flatMap((part) => {
6081
+ const variable = /^var\(\s*(--[^\s,()]+)\s*(?:,[\s\S]*)?\)$/i.exec(part.trim());
6082
+ if (!variable) {
6083
+ const name = normalizeUsedFontName(part);
6084
+ return name ? [name] : [];
6085
+ }
6086
+ const property = variable[1];
6087
+ if (!property || resolving.has(property) || resolving.size >= 8) return [];
6088
+ const referenced = properties.get(property);
6089
+ if (referenced === void 0) return [];
6090
+ return resolveUsedFontNames(referenced, properties, new Set(resolving).add(property));
6091
+ });
6092
+ }
6047
6093
  function extractUsedFontFamilies(styles) {
6048
6094
  const used = [];
6049
6095
  const seen = /* @__PURE__ */ new Set();
6050
- const propRe = /font-family\s*:\s*([^;}{]+)/gi;
6096
+ const properties = collectFontCustomProperties(styles);
6051
6097
  for (const style of styles) {
6052
- const withoutFontFace = stripCssComments2(style.content).replace(/@font-face\s*\{[^}]*\}/gi, "");
6053
- let match;
6054
- while ((match = propRe.exec(withoutFontFace)) !== null) {
6055
- for (const part of match[1].split(",")) {
6056
- const name = normalizeUsedFontName(part);
6057
- if (name && !GENERIC_FAMILIES.has(name) && !seen.has(name)) {
6098
+ parseFontCss(style.content)?.walkDecls(/^font-family$/i, (decl) => {
6099
+ if (isFontFaceDeclaration(decl)) return;
6100
+ for (const name of resolveUsedFontNames(decl.value, properties)) {
6101
+ if (!GENERIC_FAMILIES.has(name) && !seen.has(name)) {
6058
6102
  seen.add(name);
6059
6103
  used.push(name);
6060
6104
  }
6061
6105
  }
6062
- }
6106
+ });
6063
6107
  }
6064
6108
  return used;
6065
6109
  }
@@ -6162,15 +6206,12 @@ var fontRules = [
6162
6206
  ];
6163
6207
  function uniqueFontLocation(styles, families, locate) {
6164
6208
  const locations = [];
6209
+ const properties = collectFontCustomProperties(styles);
6165
6210
  for (const style of styles) {
6166
6211
  try {
6167
6212
  postcss3.parse(style.content).walkDecls(/^font-family$/i, (decl) => {
6168
- let parent = decl.parent;
6169
- while (parent) {
6170
- if (parent.type === "atrule" && parent.name.toLowerCase() === "font-face") return;
6171
- parent = parent.parent;
6172
- }
6173
- if (!decl.value.split(",").some((part) => families.includes(normalizeUsedFontName(part) ?? "")))
6213
+ if (isFontFaceDeclaration(decl)) return;
6214
+ if (!resolveUsedFontNames(decl.value, properties).some((name) => families.includes(name)))
6174
6215
  return;
6175
6216
  locations.push(locate(style, decl.source?.start?.offset));
6176
6217
  });
@@ -6178,7 +6219,7 @@ function uniqueFontLocation(styles, families, locate) {
6178
6219
  return {};
6179
6220
  }
6180
6221
  }
6181
- return locations.length === 1 ? locations[0] : {};
6222
+ return locations.length === 1 ? locations[0] ?? {} : {};
6182
6223
  }
6183
6224
 
6184
6225
  // src/rules/slideshow.ts
package/dist/index.d.ts CHANGED
@@ -98,6 +98,6 @@ interface ProjectLintResult {
98
98
  totalWarnings: number;
99
99
  totalInfos: number;
100
100
  }
101
- declare function lintProject(projectDir: string, entryFile?: string, hostOptions?: Pick<HyperframeLinterOptions, "host">): Promise<ProjectLintResult>;
101
+ declare function lintProject(projectDir: string, entryFile?: string, hostOptions?: Pick<HyperframeLinterOptions, "host" | "isSubComposition">): Promise<ProjectLintResult>;
102
102
 
103
103
  export { type HyperframeLintFinding, type HyperframeLintResult, type HyperframeLintSeverity, type HyperframeLinterOptions, LINT_RULE_COUNT, LINT_RULE_GROUP_COUNTS, type LintTimings, type ProjectLintResult, lintHyperframeHtml, lintMediaUrls, lintProject, shouldBlockRender };
package/dist/index.js CHANGED
@@ -2624,7 +2624,11 @@ function containingCallOffset(script, tokenOffset) {
2624
2624
  }
2625
2625
 
2626
2626
  // src/rules/composition.ts
2627
- import { COMPOSITION_VARIABLE_TYPES, isSafeMediaUrl } from "@hyperframes/parsers/composition";
2627
+ import {
2628
+ COMPOSITION_VARIABLE_TYPES,
2629
+ isCompositionVariable,
2630
+ isSafeMediaUrl
2631
+ } from "@hyperframes/parsers/composition";
2628
2632
  import { COMPOSITION_ATTRIBUTES, readClipTiming } from "@hyperframes/parsers/composition-contract";
2629
2633
  import { resolveCompositionDuration } from "@hyperframes/parsers/composition-duration";
2630
2634
  import {
@@ -2928,6 +2932,16 @@ function variablesDeclarationFindings(tag, tags) {
2928
2932
  });
2929
2933
  continue;
2930
2934
  }
2935
+ if (!isCompositionVariable(e)) {
2936
+ findings.push({
2937
+ code: "invalid_composition_variables_declaration",
2938
+ severity: "error",
2939
+ message: `data-composition-variables entry [${i}] has a default that does not match its type, or an enum without an options array.`,
2940
+ fixHint: "Use number defaults for number variables, boolean defaults for boolean variables, and string defaults for other types. Enum variables also require an options array.",
2941
+ snippet: truncateSnippet(tag.raw)
2942
+ });
2943
+ continue;
2944
+ }
2931
2945
  const id = String(e.id);
2932
2946
  if ((e.type === "image" || varSrcIds.has(id)) && typeof e.default === "string" && e.default.length > 0 && !isSafeMediaUrl(e.default)) {
2933
2947
  findings.push({
@@ -4966,7 +4980,7 @@ ${right.raw}`)
4966
4980
  return findings;
4967
4981
  },
4968
4982
  // gsap_timeline_registered_before_async_build — registering window.__timelines[id]
4969
- // BEFORE the timeline is built inside document.fonts.ready (or any async callback)
4983
+ // BEFORE the timeline is built inside a font-ready callback
4970
4984
  // leaves an EMPTY timeline registered. The runtime's sub-composition readiness gate
4971
4985
  // treats "key present" as "ready" and nests the child ONCE, while still empty — so the
4972
4986
  // animation never renders when this composition is mounted as a sub-composition.
@@ -4975,9 +4989,11 @@ ${right.raw}`)
4975
4989
  const findings = [];
4976
4990
  for (const script of scripts) {
4977
4991
  const content = stripJsComments(script.content);
4978
- const regIdx = content.search(/window\s*\.\s*__timelines\s*\[/);
4992
+ const regIdx = content.search(
4993
+ /window\s*\.\s*__timelines\s*(?:\[[^\]]+\]|\.\s*[A-Za-z_$][\w$]*)\s*=\s*(?!=)/
4994
+ );
4979
4995
  if (regIdx < 0) continue;
4980
- const fontsReadyIdx = content.search(/document\s*\.\s*fonts\s*\.\s*ready/);
4996
+ const fontsReadyIdx = content.search(/document\s*\.\s*fonts\s*\.\s*(?:ready\b|load\s*\()/);
4981
4997
  if (fontsReadyIdx < 0) continue;
4982
4998
  if (regIdx >= fontsReadyIdx) continue;
4983
4999
  const tail = content.slice(fontsReadyIdx);
@@ -4985,8 +5001,8 @@ ${right.raw}`)
4985
5001
  findings.push({
4986
5002
  code: "gsap_timeline_registered_before_async_build",
4987
5003
  severity: "error",
4988
- message: 'window.__timelines is assigned BEFORE the timeline is built inside document.fonts.ready. An empty timeline registered early gets nested empty when this composition is used as a sub-composition (the readiness gate treats "key present" as "ready" and never re-nests), so the animation renders blank.',
4989
- fixHint: "Move the `window.__timelines[id] = tl;` assignment to the END of the document.fonts.ready callback, after the tweens are added. Optionally call window.__hfForceTimelineRebind() right after, to re-nest the populated timeline."
5004
+ message: 'window.__timelines is assigned BEFORE the timeline is built inside document.fonts.ready or document.fonts.load(). An empty timeline registered early gets nested empty when this composition is used as a sub-composition (the readiness gate treats "key present" as "ready" and never re-nests), so the animation renders blank.',
5005
+ fixHint: "Move the `window.__timelines[id] = tl;` assignment to the END of the font-ready callback, after the tweens are added. Optionally call window.__hfForceTimelineRebind() right after, to re-nest the populated timeline."
4990
5006
  });
4991
5007
  }
4992
5008
  return findings;
@@ -6026,22 +6042,29 @@ var GENERIC_FAMILIES = /* @__PURE__ */ new Set([
6026
6042
  "unset",
6027
6043
  "revert"
6028
6044
  ]);
6029
- function stripCssComments2(css) {
6030
- return css.replace(/\/\*[\s\S]*?\*\//g, " ");
6045
+ function parseFontCss(css) {
6046
+ try {
6047
+ return postcss3.parse(css);
6048
+ } catch {
6049
+ return null;
6050
+ }
6051
+ }
6052
+ function isFontFaceDeclaration(decl) {
6053
+ let parent = decl.parent;
6054
+ while (parent) {
6055
+ if (parent.type === "atrule" && parent.name.toLowerCase() === "font-face") return true;
6056
+ parent = parent.parent;
6057
+ }
6058
+ return false;
6031
6059
  }
6032
6060
  function extractFontFaceFamilies(styles) {
6033
6061
  const families = /* @__PURE__ */ new Set();
6034
- const fontFaceRe = /@font-face\s*\{[^}]*\}/gi;
6035
- const familyRe = /font-family\s*:\s*(['"]?)([^;'"]+)\1/i;
6036
6062
  for (const style of styles) {
6037
- const content = stripCssComments2(style.content);
6038
- let match;
6039
- while ((match = fontFaceRe.exec(content)) !== null) {
6040
- const familyMatch = match[0].match(familyRe);
6041
- if (familyMatch?.[2]) {
6042
- families.add(familyMatch[2].trim().toLowerCase());
6043
- }
6044
- }
6063
+ parseFontCss(style.content)?.walkDecls(/^font-family$/i, (decl) => {
6064
+ if (!isFontFaceDeclaration(decl)) return;
6065
+ const name = normalizeUsedFontName(decl.value);
6066
+ if (name) families.add(name);
6067
+ });
6045
6068
  }
6046
6069
  return families;
6047
6070
  }
@@ -6050,22 +6073,43 @@ function normalizeUsedFontName(part) {
6050
6073
  if (!name || name.includes("(") || name.includes(")")) return null;
6051
6074
  return name;
6052
6075
  }
6076
+ function collectFontCustomProperties(styles) {
6077
+ const properties = /* @__PURE__ */ new Map();
6078
+ for (const style of styles) {
6079
+ parseFontCss(style.content)?.walkDecls(/^--/, (decl) => {
6080
+ if (!isFontFaceDeclaration(decl)) properties.set(decl.prop, decl.value);
6081
+ });
6082
+ }
6083
+ return properties;
6084
+ }
6085
+ function resolveUsedFontNames(value, properties, resolving = /* @__PURE__ */ new Set()) {
6086
+ return postcss3.list.comma(value).flatMap((part) => {
6087
+ const variable = /^var\(\s*(--[^\s,()]+)\s*(?:,[\s\S]*)?\)$/i.exec(part.trim());
6088
+ if (!variable) {
6089
+ const name = normalizeUsedFontName(part);
6090
+ return name ? [name] : [];
6091
+ }
6092
+ const property = variable[1];
6093
+ if (!property || resolving.has(property) || resolving.size >= 8) return [];
6094
+ const referenced = properties.get(property);
6095
+ if (referenced === void 0) return [];
6096
+ return resolveUsedFontNames(referenced, properties, new Set(resolving).add(property));
6097
+ });
6098
+ }
6053
6099
  function extractUsedFontFamilies(styles) {
6054
6100
  const used = [];
6055
6101
  const seen = /* @__PURE__ */ new Set();
6056
- const propRe = /font-family\s*:\s*([^;}{]+)/gi;
6102
+ const properties = collectFontCustomProperties(styles);
6057
6103
  for (const style of styles) {
6058
- const withoutFontFace = stripCssComments2(style.content).replace(/@font-face\s*\{[^}]*\}/gi, "");
6059
- let match;
6060
- while ((match = propRe.exec(withoutFontFace)) !== null) {
6061
- for (const part of match[1].split(",")) {
6062
- const name = normalizeUsedFontName(part);
6063
- if (name && !GENERIC_FAMILIES.has(name) && !seen.has(name)) {
6104
+ parseFontCss(style.content)?.walkDecls(/^font-family$/i, (decl) => {
6105
+ if (isFontFaceDeclaration(decl)) return;
6106
+ for (const name of resolveUsedFontNames(decl.value, properties)) {
6107
+ if (!GENERIC_FAMILIES.has(name) && !seen.has(name)) {
6064
6108
  seen.add(name);
6065
6109
  used.push(name);
6066
6110
  }
6067
6111
  }
6068
- }
6112
+ });
6069
6113
  }
6070
6114
  return used;
6071
6115
  }
@@ -6168,15 +6212,12 @@ var fontRules = [
6168
6212
  ];
6169
6213
  function uniqueFontLocation(styles, families, locate) {
6170
6214
  const locations = [];
6215
+ const properties = collectFontCustomProperties(styles);
6171
6216
  for (const style of styles) {
6172
6217
  try {
6173
6218
  postcss3.parse(style.content).walkDecls(/^font-family$/i, (decl) => {
6174
- let parent = decl.parent;
6175
- while (parent) {
6176
- if (parent.type === "atrule" && parent.name.toLowerCase() === "font-face") return;
6177
- parent = parent.parent;
6178
- }
6179
- if (!decl.value.split(",").some((part) => families.includes(normalizeUsedFontName(part) ?? "")))
6219
+ if (isFontFaceDeclaration(decl)) return;
6220
+ if (!resolveUsedFontNames(decl.value, properties).some((name) => families.includes(name)))
6180
6221
  return;
6181
6222
  locations.push(locate(style, decl.source?.start?.offset));
6182
6223
  });
@@ -6184,7 +6225,7 @@ function uniqueFontLocation(styles, families, locate) {
6184
6225
  return {};
6185
6226
  }
6186
6227
  }
6187
- return locations.length === 1 ? locations[0] : {};
6228
+ return locations.length === 1 ? locations[0] ?? {} : {};
6188
6229
  }
6189
6230
 
6190
6231
  // src/rules/slideshow.ts
@@ -6750,6 +6791,7 @@ import {
6750
6791
  } from "@hyperframes/parsers/reference-resolver";
6751
6792
  import { checkSubCompositionUsability } from "@hyperframes/parsers/sub-composition-validity";
6752
6793
  import { parseHTML as parseHTML3 } from "linkedom";
6794
+ import postcss4 from "postcss";
6753
6795
  import {
6754
6796
  cleanAssetUrl as cleanAssetUrl2,
6755
6797
  collectSubCompositionSrcs,
@@ -7070,6 +7112,7 @@ function contentDigest(html) {
7070
7112
  return createHash("sha256").update(html).digest("hex").slice(0, 16);
7071
7113
  }
7072
7114
  var AUDIO_EXTENSIONS = /* @__PURE__ */ new Set([".mp3", ".wav", ".aac", ".ogg", ".m4a", ".flac", ".opus"]);
7115
+ var CSS_URL_RE = /\burl\(\s*(?:"([^"]+)"|'([^']+)'|([^"')\s]+))\s*\)/gi;
7073
7116
  var MASK_IMAGE_URL_RE = /\b(?:-webkit-)?mask-image\s*:\s*[^;{}]*url\(\s*(?:"([^"]+)"|'([^']+)'|([^"')\s]+))\s*\)/gi;
7074
7117
  function isLocalStylesheetHref(href) {
7075
7118
  return !!href && !/^(https?:|data:|blob:|\/\/)/i.test(href);
@@ -7362,9 +7405,16 @@ function lintTextureMaskAssetNotFound(projectDir, htmlSources) {
7362
7405
  for (const { html, compSrcPath } of htmlSources) {
7363
7406
  const file = sourceFile(projectDir, compSrcPath);
7364
7407
  for (const cssSource of collectCssSources(projectDir, html, compSrcPath)) {
7365
- let match;
7366
- const pattern = new RegExp(MASK_IMAGE_URL_RE.source, MASK_IMAGE_URL_RE.flags);
7367
- while ((match = pattern.exec(cssSource.content)) !== null) {
7408
+ let matches = [];
7409
+ try {
7410
+ postcss4.parse(cssSource.content).walkDecls(/^(?:-webkit-)?mask-image$/i, (declaration) => {
7411
+ matches.push(...declaration.value.matchAll(CSS_URL_RE));
7412
+ });
7413
+ } catch (error) {
7414
+ if (!(error instanceof postcss4.CssSyntaxError)) throw error;
7415
+ matches = [...cssSource.content.matchAll(MASK_IMAGE_URL_RE)];
7416
+ }
7417
+ for (const match of matches) {
7368
7418
  const rawUrl = match[1] ?? match[2] ?? match[3] ?? "";
7369
7419
  if (isUnresolvedAssetPlaceholder2(rawUrl)) continue;
7370
7420
  const url = cleanAssetUrl2(rawUrl);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/lint",
3
- "version": "0.8.191",
3
+ "version": "0.8.192",
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.191"
38
+ "@hyperframes/parsers": "0.8.192"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/node": "^25.0.10",