@hyperframes/lint 0.8.207 → 0.8.208

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
@@ -4117,6 +4117,23 @@ ${allInlineStyles}`.replace(/\/\*[\s\S]*?\*\//g, "");
4117
4117
  snippet: truncateSnippet(rootTag.raw)
4118
4118
  }
4119
4119
  ];
4120
+ },
4121
+ // subcomposition_linked_style_names_root_id
4122
+ // The compiler flattens a sub-composition root into its host and rewrites inline <style>
4123
+ // selectors to match; a linked stylesheet is not rewritten, so `#root-id` in it matches nothing.
4124
+ ({ styles, rootTag, options }) => {
4125
+ if (!options.isSubComposition || !rootTag) return [];
4126
+ const rootId = readAttr(rootTag.raw, "id");
4127
+ if (!rootId) return [];
4128
+ const escaped = rootId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4129
+ const names = new RegExp(`#${escaped}(?![\\w-])`);
4130
+ return styles.filter((style) => style.index === -1 && names.test(stripCssComments(style.content))).map((style) => ({
4131
+ code: "subcomposition_linked_style_names_root_id",
4132
+ severity: "warning",
4133
+ message: `The linked stylesheet "${style.rootRelativePath ?? style.file}" has a rule on #${rootId}, the id of this sub-composition's root. The root is merged into its host when the composition is compiled, so that selector matches nothing (the same rule in an inline <style> is rewritten and works).`,
4134
+ fixHint: `Move the #${rootId} rules into a <style> inside the <template>, where they are rewritten for the merged root, or select a child element of the root in the linked file instead of the root itself.`,
4135
+ snippet: truncateSnippet(style.content.slice(0, 120))
4136
+ }));
4120
4137
  }
4121
4138
  ];
4122
4139
  var CLIP_MEDIA_TAGS = /* @__PURE__ */ new Set(["img", "video", "audio"]);
@@ -6872,6 +6889,68 @@ var shaderRules = [
6872
6889
  })
6873
6890
  ];
6874
6891
 
6892
+ // src/rules/rootPaths.ts
6893
+ var URL_ATTRS = ["src", "href", "poster", "xlink:href"];
6894
+ var ROOT_PATH = /^\/(?!\/)/;
6895
+ var CSS_ROOT_URL = /\burl\(\s*["']?(\/(?!\/)[^"')\s]*)/g;
6896
+ var SCRIPT_ROOT_URL = /\b(?:fetch|new\s+URL)\(\s*(["'`])(\/(?!\/)[^"'`$]*)\1/g;
6897
+ function relativeForm(path, cssFile) {
6898
+ const depth = (cssFile ?? "").split("/").filter(Boolean).length - 1;
6899
+ return `${"../".repeat(Math.max(depth, 0))}${path.slice(1)}`;
6900
+ }
6901
+ function rootPathFinding(path, cssFile, snippet) {
6902
+ return {
6903
+ code: "root_path_missing_in_preview",
6904
+ severity: "warning",
6905
+ message: `"${path}" starts at the server's root. Render and snapshot serve the project there, but Studio's preview serves it under the project's own path, so the preview cannot find this file.`,
6906
+ fixHint: `Use "${relativeForm(path, cssFile)}" instead.`,
6907
+ snippet: truncateSnippet(snippet)
6908
+ };
6909
+ }
6910
+ function cssRootPaths(css, cssFile) {
6911
+ return [...css.matchAll(CSS_ROOT_URL)].map(
6912
+ (match) => rootPathFinding(match[1] ?? "", cssFile, match[0])
6913
+ );
6914
+ }
6915
+ function tagRootPaths(tag) {
6916
+ if (tag.name === "a" || tag.name === "base") return [];
6917
+ const paths = URL_ATTRS.map((attr) => readAttr(tag.raw, attr)?.trim() ?? "").filter(
6918
+ (value) => ROOT_PATH.test(value)
6919
+ );
6920
+ return [
6921
+ ...paths.map((path) => rootPathFinding(path, void 0, tag.raw)),
6922
+ ...cssRootPaths(readAttr(tag.raw, "style") ?? "")
6923
+ ];
6924
+ }
6925
+ var scriptRootPaths = (script) => [...script.matchAll(SCRIPT_ROOT_URL)].map(
6926
+ (match) => rootPathFinding(match[2] ?? "", void 0, match[0])
6927
+ );
6928
+ function invalidImportMap(script) {
6929
+ if (!/\btype\s*=\s*["']?importmap\b/i.test(script.attrs)) return [];
6930
+ try {
6931
+ JSON.parse(script.content);
6932
+ return [];
6933
+ } catch (error) {
6934
+ return [
6935
+ {
6936
+ code: "import_map_invalid",
6937
+ severity: "warning",
6938
+ message: `This import map is not valid JSON (${error instanceof Error ? error.message : String(error)}). The browser ignores it, and preview cannot map root module paths ("/assets/x.js") to the project.`,
6939
+ fixHint: "Make the import map valid JSON: double-quoted keys and values, no trailing commas.",
6940
+ snippet: truncateSnippet(script.content)
6941
+ }
6942
+ ];
6943
+ }
6944
+ }
6945
+ var rootPathRules = [
6946
+ (ctx) => [
6947
+ ...ctx.tags.flatMap(tagRootPaths),
6948
+ ...ctx.styles.flatMap((style) => cssRootPaths(style.content, style.rootRelativePath)),
6949
+ ...ctx.scripts.flatMap((script) => scriptRootPaths(script.content)),
6950
+ ...ctx.scripts.flatMap(invalidImportMap)
6951
+ ]
6952
+ ];
6953
+
6875
6954
  // src/hyperframeLinter.ts
6876
6955
  var RULE_GROUPS = [
6877
6956
  { group: "core", rules: coreRules },
@@ -6884,7 +6963,8 @@ var RULE_GROUPS = [
6884
6963
  { group: "fonts", rules: fontRules },
6885
6964
  { group: "slideshow", rules: slideshowRules },
6886
6965
  { group: "structure", rules: structureRules },
6887
- { group: "shaders", rules: shaderRules }
6966
+ { group: "shaders", rules: shaderRules },
6967
+ { group: "rootPaths", rules: rootPathRules }
6888
6968
  ];
6889
6969
  var LINT_RULE_COUNT = RULE_GROUPS.reduce((n, g) => n + g.rules.length, 0);
6890
6970
  var LINT_RULE_GROUP_COUNTS = Object.fromEntries(
package/dist/index.js CHANGED
@@ -4123,6 +4123,23 @@ ${allInlineStyles}`.replace(/\/\*[\s\S]*?\*\//g, "");
4123
4123
  snippet: truncateSnippet(rootTag.raw)
4124
4124
  }
4125
4125
  ];
4126
+ },
4127
+ // subcomposition_linked_style_names_root_id
4128
+ // The compiler flattens a sub-composition root into its host and rewrites inline <style>
4129
+ // selectors to match; a linked stylesheet is not rewritten, so `#root-id` in it matches nothing.
4130
+ ({ styles, rootTag, options }) => {
4131
+ if (!options.isSubComposition || !rootTag) return [];
4132
+ const rootId = readAttr(rootTag.raw, "id");
4133
+ if (!rootId) return [];
4134
+ const escaped = rootId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4135
+ const names = new RegExp(`#${escaped}(?![\\w-])`);
4136
+ return styles.filter((style) => style.index === -1 && names.test(stripCssComments(style.content))).map((style) => ({
4137
+ code: "subcomposition_linked_style_names_root_id",
4138
+ severity: "warning",
4139
+ message: `The linked stylesheet "${style.rootRelativePath ?? style.file}" has a rule on #${rootId}, the id of this sub-composition's root. The root is merged into its host when the composition is compiled, so that selector matches nothing (the same rule in an inline <style> is rewritten and works).`,
4140
+ fixHint: `Move the #${rootId} rules into a <style> inside the <template>, where they are rewritten for the merged root, or select a child element of the root in the linked file instead of the root itself.`,
4141
+ snippet: truncateSnippet(style.content.slice(0, 120))
4142
+ }));
4126
4143
  }
4127
4144
  ];
4128
4145
  var CLIP_MEDIA_TAGS = /* @__PURE__ */ new Set(["img", "video", "audio"]);
@@ -6878,6 +6895,68 @@ var shaderRules = [
6878
6895
  })
6879
6896
  ];
6880
6897
 
6898
+ // src/rules/rootPaths.ts
6899
+ var URL_ATTRS = ["src", "href", "poster", "xlink:href"];
6900
+ var ROOT_PATH = /^\/(?!\/)/;
6901
+ var CSS_ROOT_URL = /\burl\(\s*["']?(\/(?!\/)[^"')\s]*)/g;
6902
+ var SCRIPT_ROOT_URL = /\b(?:fetch|new\s+URL)\(\s*(["'`])(\/(?!\/)[^"'`$]*)\1/g;
6903
+ function relativeForm(path, cssFile) {
6904
+ const depth = (cssFile ?? "").split("/").filter(Boolean).length - 1;
6905
+ return `${"../".repeat(Math.max(depth, 0))}${path.slice(1)}`;
6906
+ }
6907
+ function rootPathFinding(path, cssFile, snippet) {
6908
+ return {
6909
+ code: "root_path_missing_in_preview",
6910
+ severity: "warning",
6911
+ message: `"${path}" starts at the server's root. Render and snapshot serve the project there, but Studio's preview serves it under the project's own path, so the preview cannot find this file.`,
6912
+ fixHint: `Use "${relativeForm(path, cssFile)}" instead.`,
6913
+ snippet: truncateSnippet(snippet)
6914
+ };
6915
+ }
6916
+ function cssRootPaths(css, cssFile) {
6917
+ return [...css.matchAll(CSS_ROOT_URL)].map(
6918
+ (match) => rootPathFinding(match[1] ?? "", cssFile, match[0])
6919
+ );
6920
+ }
6921
+ function tagRootPaths(tag) {
6922
+ if (tag.name === "a" || tag.name === "base") return [];
6923
+ const paths = URL_ATTRS.map((attr) => readAttr(tag.raw, attr)?.trim() ?? "").filter(
6924
+ (value) => ROOT_PATH.test(value)
6925
+ );
6926
+ return [
6927
+ ...paths.map((path) => rootPathFinding(path, void 0, tag.raw)),
6928
+ ...cssRootPaths(readAttr(tag.raw, "style") ?? "")
6929
+ ];
6930
+ }
6931
+ var scriptRootPaths = (script) => [...script.matchAll(SCRIPT_ROOT_URL)].map(
6932
+ (match) => rootPathFinding(match[2] ?? "", void 0, match[0])
6933
+ );
6934
+ function invalidImportMap(script) {
6935
+ if (!/\btype\s*=\s*["']?importmap\b/i.test(script.attrs)) return [];
6936
+ try {
6937
+ JSON.parse(script.content);
6938
+ return [];
6939
+ } catch (error) {
6940
+ return [
6941
+ {
6942
+ code: "import_map_invalid",
6943
+ severity: "warning",
6944
+ message: `This import map is not valid JSON (${error instanceof Error ? error.message : String(error)}). The browser ignores it, and preview cannot map root module paths ("/assets/x.js") to the project.`,
6945
+ fixHint: "Make the import map valid JSON: double-quoted keys and values, no trailing commas.",
6946
+ snippet: truncateSnippet(script.content)
6947
+ }
6948
+ ];
6949
+ }
6950
+ }
6951
+ var rootPathRules = [
6952
+ (ctx) => [
6953
+ ...ctx.tags.flatMap(tagRootPaths),
6954
+ ...ctx.styles.flatMap((style) => cssRootPaths(style.content, style.rootRelativePath)),
6955
+ ...ctx.scripts.flatMap((script) => scriptRootPaths(script.content)),
6956
+ ...ctx.scripts.flatMap(invalidImportMap)
6957
+ ]
6958
+ ];
6959
+
6881
6960
  // src/hyperframeLinter.ts
6882
6961
  var RULE_GROUPS = [
6883
6962
  { group: "core", rules: coreRules },
@@ -6890,7 +6969,8 @@ var RULE_GROUPS = [
6890
6969
  { group: "fonts", rules: fontRules },
6891
6970
  { group: "slideshow", rules: slideshowRules },
6892
6971
  { group: "structure", rules: structureRules },
6893
- { group: "shaders", rules: shaderRules }
6972
+ { group: "shaders", rules: shaderRules },
6973
+ { group: "rootPaths", rules: rootPathRules }
6894
6974
  ];
6895
6975
  var LINT_RULE_COUNT = RULE_GROUPS.reduce((n, g) => n + g.rules.length, 0);
6896
6976
  var LINT_RULE_GROUP_COUNTS = Object.fromEntries(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/lint",
3
- "version": "0.8.207",
3
+ "version": "0.8.208",
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.207"
38
+ "@hyperframes/parsers": "0.8.208"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/node": "^25.0.10",