@hyperframes/lint 0.9.16 → 0.9.18
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 +37 -25
- package/dist/index.d.ts +7 -2
- package/dist/index.js +97 -64
- package/package.json +3 -2
package/dist/browser.js
CHANGED
|
@@ -2797,6 +2797,9 @@ function findAudioGroupCarveAttrFindings(ctx) {
|
|
|
2797
2797
|
return findings;
|
|
2798
2798
|
}
|
|
2799
2799
|
|
|
2800
|
+
// src/rules/gsap.ts
|
|
2801
|
+
import postcss2 from "postcss";
|
|
2802
|
+
|
|
2800
2803
|
// src/scriptPositions.ts
|
|
2801
2804
|
import { parse as parse3 } from "acorn";
|
|
2802
2805
|
import { simple as simple3 } from "acorn-walk";
|
|
@@ -4920,17 +4923,30 @@ function indexInsideNonIifeRange(index, source, ranges) {
|
|
|
4920
4923
|
(range) => index > range.start && index < range.end && !isIifeBody(source, range)
|
|
4921
4924
|
);
|
|
4922
4925
|
}
|
|
4926
|
+
function cssRuleSubjects(styles) {
|
|
4927
|
+
const rules = [];
|
|
4928
|
+
for (const style of styles) {
|
|
4929
|
+
let root;
|
|
4930
|
+
try {
|
|
4931
|
+
root = postcss2.parse(style.content);
|
|
4932
|
+
} catch {
|
|
4933
|
+
continue;
|
|
4934
|
+
}
|
|
4935
|
+
root.walkRules((rule) => {
|
|
4936
|
+
const subjects = rule.selectors.flatMap(
|
|
4937
|
+
(selector) => /[#.][a-zA-Z0-9_-]+$/.exec(selector.trim())?.[0] ?? []
|
|
4938
|
+
);
|
|
4939
|
+
if (subjects.length) rules.push({ declarations: rule.nodes.map(String), subjects });
|
|
4940
|
+
});
|
|
4941
|
+
}
|
|
4942
|
+
return rules;
|
|
4943
|
+
}
|
|
4923
4944
|
function collectCssOpacityZeroSelectors(styles, tags) {
|
|
4924
4945
|
const selectors = /* @__PURE__ */ new Set();
|
|
4925
4946
|
const opacityExactlyZero = /opacity\s*:\s*0(?:\.0+)?\s*(?:;|$)/;
|
|
4926
|
-
for (const
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
)) {
|
|
4930
|
-
if (body && opacityExactlyZero.test(body)) {
|
|
4931
|
-
selectors.add((selector ?? "").trim());
|
|
4932
|
-
}
|
|
4933
|
-
}
|
|
4947
|
+
for (const { declarations, subjects } of cssRuleSubjects(styles)) {
|
|
4948
|
+
if (!declarations.some((declaration) => opacityExactlyZero.test(declaration))) continue;
|
|
4949
|
+
for (const subject of subjects) selectors.add(subject);
|
|
4934
4950
|
}
|
|
4935
4951
|
for (const tag of tags) {
|
|
4936
4952
|
const inlineStyle = readAttr(tag.raw, "style");
|
|
@@ -5138,17 +5154,13 @@ ${right.raw}`)
|
|
|
5138
5154
|
const findings = [];
|
|
5139
5155
|
const cssTranslateSelectors = /* @__PURE__ */ new Map();
|
|
5140
5156
|
const cssScaleSelectors = /* @__PURE__ */ new Map();
|
|
5141
|
-
for (const
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
)
|
|
5145
|
-
|
|
5146
|
-
if (
|
|
5147
|
-
|
|
5148
|
-
if (/translate/i.test(transformVal))
|
|
5149
|
-
cssTranslateSelectors.set((selector ?? "").trim(), transformVal);
|
|
5150
|
-
if (/scale/i.test(transformVal))
|
|
5151
|
-
cssScaleSelectors.set((selector ?? "").trim(), transformVal);
|
|
5157
|
+
for (const { declarations, subjects } of cssRuleSubjects(styles)) {
|
|
5158
|
+
const tMatch = declarations.join(";").match(/transform\s*:\s*([^;]+)/);
|
|
5159
|
+
if (!tMatch || !tMatch[1]) continue;
|
|
5160
|
+
const transformVal = tMatch[1].trim();
|
|
5161
|
+
for (const subject of subjects) {
|
|
5162
|
+
if (/translate/i.test(transformVal)) cssTranslateSelectors.set(subject, transformVal);
|
|
5163
|
+
if (/scale/i.test(transformVal)) cssScaleSelectors.set(subject, transformVal);
|
|
5152
5164
|
}
|
|
5153
5165
|
}
|
|
5154
5166
|
for (const tag of tags) {
|
|
@@ -6308,7 +6320,7 @@ var adapterRules = [
|
|
|
6308
6320
|
];
|
|
6309
6321
|
|
|
6310
6322
|
// src/rules/textures.ts
|
|
6311
|
-
import
|
|
6323
|
+
import postcss3 from "postcss";
|
|
6312
6324
|
var TEXTURE_BASE_CLASS = "hf-texture-text";
|
|
6313
6325
|
var TEXTURE_CLASS_PREFIX = "hf-texture-";
|
|
6314
6326
|
function classNames(tag) {
|
|
@@ -6355,7 +6367,7 @@ function collectTextureCss(styles) {
|
|
|
6355
6367
|
for (const style of styles) {
|
|
6356
6368
|
let root;
|
|
6357
6369
|
try {
|
|
6358
|
-
root =
|
|
6370
|
+
root = postcss3.parse(style.content);
|
|
6359
6371
|
} catch {
|
|
6360
6372
|
continue;
|
|
6361
6373
|
}
|
|
@@ -6485,7 +6497,7 @@ var textureRules = [
|
|
|
6485
6497
|
];
|
|
6486
6498
|
|
|
6487
6499
|
// src/rules/fonts.ts
|
|
6488
|
-
import
|
|
6500
|
+
import postcss4 from "postcss";
|
|
6489
6501
|
import {
|
|
6490
6502
|
FONT_ALIAS_KEYS,
|
|
6491
6503
|
parseFontShorthand,
|
|
@@ -6518,7 +6530,7 @@ var GENERIC_FAMILIES = /* @__PURE__ */ new Set([
|
|
|
6518
6530
|
]);
|
|
6519
6531
|
function parseFontCss(css) {
|
|
6520
6532
|
try {
|
|
6521
|
-
return
|
|
6533
|
+
return postcss4.parse(css);
|
|
6522
6534
|
} catch {
|
|
6523
6535
|
return null;
|
|
6524
6536
|
}
|
|
@@ -6560,7 +6572,7 @@ function collectFontCustomProperties(styles) {
|
|
|
6560
6572
|
return properties;
|
|
6561
6573
|
}
|
|
6562
6574
|
function resolveUsedFontNames(value, properties, resolving = /* @__PURE__ */ new Set()) {
|
|
6563
|
-
return
|
|
6575
|
+
return postcss4.list.comma(value).flatMap((part) => {
|
|
6564
6576
|
const variable = /^var\(\s*(--[^\s,()]+)\s*(?:,[\s\S]*)?\)$/i.exec(part.trim());
|
|
6565
6577
|
if (!variable) {
|
|
6566
6578
|
const name = normalizeUsedFontName(part);
|
|
@@ -6722,7 +6734,7 @@ function uniqueFontLocation(styles, families, locate) {
|
|
|
6722
6734
|
const properties = collectFontCustomProperties(styles);
|
|
6723
6735
|
for (const style of styles) {
|
|
6724
6736
|
try {
|
|
6725
|
-
|
|
6737
|
+
postcss4.parse(style.content).walkDecls(FONT_FAMILY_PROPS, (decl) => {
|
|
6726
6738
|
const named = resolveUsedFontNames(usedFamilyList(decl), properties);
|
|
6727
6739
|
if (!named.some((name) => families.includes(name))) return;
|
|
6728
6740
|
locations.push(locate(style, decl.source?.start?.offset));
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Ignore } from 'ignore';
|
|
2
|
+
|
|
1
3
|
type HyperframeLintSeverity = "error" | "warning" | "info";
|
|
2
4
|
type HyperframeLintFinding = {
|
|
3
5
|
code: string;
|
|
@@ -106,9 +108,12 @@ interface ProjectLintResult {
|
|
|
106
108
|
totalWarnings: number;
|
|
107
109
|
totalInfos: number;
|
|
108
110
|
}
|
|
109
|
-
declare function
|
|
111
|
+
declare function linkedSources(projectDir: string, html: string, compSrcPath?: string): Pick<HyperframeLinterOptions, "externalStyles" | "externalScripts">;
|
|
110
112
|
declare function lintProject(projectDir: string, entryFile?: string, hostOptions?: Pick<HyperframeLinterOptions, "host" | "isSubComposition">): Promise<ProjectLintResult>;
|
|
111
113
|
|
|
114
|
+
/** What the project leaves out; ask with a relative `/` path, a folder with a trailing `/`. */
|
|
115
|
+
declare function projectIgnore(projectDir: string): Ignore;
|
|
116
|
+
|
|
112
117
|
type AudioAttribute = "data-automation" | "data-fx-chain";
|
|
113
118
|
/**
|
|
114
119
|
* Why the renderer would refuse an audio attribute's value before reading any lane or effect, or null when the
|
|
@@ -116,4 +121,4 @@ type AudioAttribute = "data-automation" | "data-fx-chain";
|
|
|
116
121
|
*/
|
|
117
122
|
declare function audioAttributeShapeError(attr: AudioAttribute, json: string): string | null;
|
|
118
123
|
|
|
119
|
-
export { type AudioAttribute, type HyperframeLintFinding, type HyperframeLintResult, type HyperframeLintSeverity, type HyperframeLinterOptions, LINT_RULE_COUNT, LINT_RULE_GROUP_COUNTS, type LintTimings, type ProjectLintResult, audioAttributeShapeError,
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -2803,6 +2803,9 @@ function findAudioGroupCarveAttrFindings(ctx) {
|
|
|
2803
2803
|
return findings;
|
|
2804
2804
|
}
|
|
2805
2805
|
|
|
2806
|
+
// src/rules/gsap.ts
|
|
2807
|
+
import postcss2 from "postcss";
|
|
2808
|
+
|
|
2806
2809
|
// src/scriptPositions.ts
|
|
2807
2810
|
import { parse as parse3 } from "acorn";
|
|
2808
2811
|
import { simple as simple3 } from "acorn-walk";
|
|
@@ -4926,17 +4929,30 @@ function indexInsideNonIifeRange(index, source, ranges) {
|
|
|
4926
4929
|
(range) => index > range.start && index < range.end && !isIifeBody(source, range)
|
|
4927
4930
|
);
|
|
4928
4931
|
}
|
|
4932
|
+
function cssRuleSubjects(styles) {
|
|
4933
|
+
const rules = [];
|
|
4934
|
+
for (const style of styles) {
|
|
4935
|
+
let root;
|
|
4936
|
+
try {
|
|
4937
|
+
root = postcss2.parse(style.content);
|
|
4938
|
+
} catch {
|
|
4939
|
+
continue;
|
|
4940
|
+
}
|
|
4941
|
+
root.walkRules((rule) => {
|
|
4942
|
+
const subjects = rule.selectors.flatMap(
|
|
4943
|
+
(selector) => /[#.][a-zA-Z0-9_-]+$/.exec(selector.trim())?.[0] ?? []
|
|
4944
|
+
);
|
|
4945
|
+
if (subjects.length) rules.push({ declarations: rule.nodes.map(String), subjects });
|
|
4946
|
+
});
|
|
4947
|
+
}
|
|
4948
|
+
return rules;
|
|
4949
|
+
}
|
|
4929
4950
|
function collectCssOpacityZeroSelectors(styles, tags) {
|
|
4930
4951
|
const selectors = /* @__PURE__ */ new Set();
|
|
4931
4952
|
const opacityExactlyZero = /opacity\s*:\s*0(?:\.0+)?\s*(?:;|$)/;
|
|
4932
|
-
for (const
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
)) {
|
|
4936
|
-
if (body && opacityExactlyZero.test(body)) {
|
|
4937
|
-
selectors.add((selector ?? "").trim());
|
|
4938
|
-
}
|
|
4939
|
-
}
|
|
4953
|
+
for (const { declarations, subjects } of cssRuleSubjects(styles)) {
|
|
4954
|
+
if (!declarations.some((declaration) => opacityExactlyZero.test(declaration))) continue;
|
|
4955
|
+
for (const subject of subjects) selectors.add(subject);
|
|
4940
4956
|
}
|
|
4941
4957
|
for (const tag of tags) {
|
|
4942
4958
|
const inlineStyle = readAttr(tag.raw, "style");
|
|
@@ -5144,17 +5160,13 @@ ${right.raw}`)
|
|
|
5144
5160
|
const findings = [];
|
|
5145
5161
|
const cssTranslateSelectors = /* @__PURE__ */ new Map();
|
|
5146
5162
|
const cssScaleSelectors = /* @__PURE__ */ new Map();
|
|
5147
|
-
for (const
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
)
|
|
5151
|
-
|
|
5152
|
-
if (
|
|
5153
|
-
|
|
5154
|
-
if (/translate/i.test(transformVal))
|
|
5155
|
-
cssTranslateSelectors.set((selector ?? "").trim(), transformVal);
|
|
5156
|
-
if (/scale/i.test(transformVal))
|
|
5157
|
-
cssScaleSelectors.set((selector ?? "").trim(), transformVal);
|
|
5163
|
+
for (const { declarations, subjects } of cssRuleSubjects(styles)) {
|
|
5164
|
+
const tMatch = declarations.join(";").match(/transform\s*:\s*([^;]+)/);
|
|
5165
|
+
if (!tMatch || !tMatch[1]) continue;
|
|
5166
|
+
const transformVal = tMatch[1].trim();
|
|
5167
|
+
for (const subject of subjects) {
|
|
5168
|
+
if (/translate/i.test(transformVal)) cssTranslateSelectors.set(subject, transformVal);
|
|
5169
|
+
if (/scale/i.test(transformVal)) cssScaleSelectors.set(subject, transformVal);
|
|
5158
5170
|
}
|
|
5159
5171
|
}
|
|
5160
5172
|
for (const tag of tags) {
|
|
@@ -6314,7 +6326,7 @@ var adapterRules = [
|
|
|
6314
6326
|
];
|
|
6315
6327
|
|
|
6316
6328
|
// src/rules/textures.ts
|
|
6317
|
-
import
|
|
6329
|
+
import postcss3 from "postcss";
|
|
6318
6330
|
var TEXTURE_BASE_CLASS = "hf-texture-text";
|
|
6319
6331
|
var TEXTURE_CLASS_PREFIX = "hf-texture-";
|
|
6320
6332
|
function classNames(tag) {
|
|
@@ -6361,7 +6373,7 @@ function collectTextureCss(styles) {
|
|
|
6361
6373
|
for (const style of styles) {
|
|
6362
6374
|
let root;
|
|
6363
6375
|
try {
|
|
6364
|
-
root =
|
|
6376
|
+
root = postcss3.parse(style.content);
|
|
6365
6377
|
} catch {
|
|
6366
6378
|
continue;
|
|
6367
6379
|
}
|
|
@@ -6491,7 +6503,7 @@ var textureRules = [
|
|
|
6491
6503
|
];
|
|
6492
6504
|
|
|
6493
6505
|
// src/rules/fonts.ts
|
|
6494
|
-
import
|
|
6506
|
+
import postcss4 from "postcss";
|
|
6495
6507
|
import {
|
|
6496
6508
|
FONT_ALIAS_KEYS,
|
|
6497
6509
|
parseFontShorthand,
|
|
@@ -6524,7 +6536,7 @@ var GENERIC_FAMILIES = /* @__PURE__ */ new Set([
|
|
|
6524
6536
|
]);
|
|
6525
6537
|
function parseFontCss(css) {
|
|
6526
6538
|
try {
|
|
6527
|
-
return
|
|
6539
|
+
return postcss4.parse(css);
|
|
6528
6540
|
} catch {
|
|
6529
6541
|
return null;
|
|
6530
6542
|
}
|
|
@@ -6566,7 +6578,7 @@ function collectFontCustomProperties(styles) {
|
|
|
6566
6578
|
return properties;
|
|
6567
6579
|
}
|
|
6568
6580
|
function resolveUsedFontNames(value, properties, resolving = /* @__PURE__ */ new Set()) {
|
|
6569
|
-
return
|
|
6581
|
+
return postcss4.list.comma(value).flatMap((part) => {
|
|
6570
6582
|
const variable = /^var\(\s*(--[^\s,()]+)\s*(?:,[\s\S]*)?\)$/i.exec(part.trim());
|
|
6571
6583
|
if (!variable) {
|
|
6572
6584
|
const name = normalizeUsedFontName(part);
|
|
@@ -6728,7 +6740,7 @@ function uniqueFontLocation(styles, families, locate) {
|
|
|
6728
6740
|
const properties = collectFontCustomProperties(styles);
|
|
6729
6741
|
for (const style of styles) {
|
|
6730
6742
|
try {
|
|
6731
|
-
|
|
6743
|
+
postcss4.parse(style.content).walkDecls(FONT_FAMILY_PROPS, (decl) => {
|
|
6732
6744
|
const named = resolveUsedFontNames(usedFamilyList(decl), properties);
|
|
6733
6745
|
if (!named.some((name) => families.includes(name))) return;
|
|
6734
6746
|
locations.push(locate(style, decl.source?.start?.offset));
|
|
@@ -7249,8 +7261,22 @@ function shouldBlockRender(strictErrors, strictAll, totalErrors, totalWarnings)
|
|
|
7249
7261
|
|
|
7250
7262
|
// src/project.ts
|
|
7251
7263
|
import { createHash } from "crypto";
|
|
7252
|
-
import { existsSync as
|
|
7253
|
-
|
|
7264
|
+
import { existsSync as existsSync3, readdirSync } from "fs";
|
|
7265
|
+
|
|
7266
|
+
// src/projectIgnore.ts
|
|
7267
|
+
import { existsSync, readFileSync } from "fs";
|
|
7268
|
+
import { join } from "path";
|
|
7269
|
+
import ignore from "ignore";
|
|
7270
|
+
var DEFAULT_PROJECT_IGNORE = ["/renders/", "/snapshots/", ".*"];
|
|
7271
|
+
function projectIgnore(projectDir) {
|
|
7272
|
+
const matcher = ignore().add(DEFAULT_PROJECT_IGNORE);
|
|
7273
|
+
const ignorePath = join(projectDir, ".hyperframesignore");
|
|
7274
|
+
if (existsSync(ignorePath)) matcher.add(readFileSync(ignorePath, "utf-8"));
|
|
7275
|
+
return matcher;
|
|
7276
|
+
}
|
|
7277
|
+
|
|
7278
|
+
// src/project.ts
|
|
7279
|
+
import { dirname as dirname2, extname, join as join4, relative as relative2, resolve } from "path";
|
|
7254
7280
|
import { rewriteAssetPath as rewriteAssetPath2 } from "@hyperframes/parsers/asset-paths";
|
|
7255
7281
|
import { spansOverlap } from "@hyperframes/parsers/time-spans";
|
|
7256
7282
|
import {
|
|
@@ -7266,7 +7292,7 @@ import {
|
|
|
7266
7292
|
} from "@hyperframes/parsers/reference-resolver";
|
|
7267
7293
|
import { checkSubCompositionUsability } from "@hyperframes/parsers/sub-composition-validity";
|
|
7268
7294
|
import { parseHTML as parseHTML3 } from "linkedom";
|
|
7269
|
-
import
|
|
7295
|
+
import postcss5 from "postcss";
|
|
7270
7296
|
import {
|
|
7271
7297
|
cleanAssetUrl as cleanAssetUrl2,
|
|
7272
7298
|
collectSubCompositionSrcs,
|
|
@@ -7284,8 +7310,8 @@ import { encodeUrlPath } from "@hyperframes/parsers";
|
|
|
7284
7310
|
|
|
7285
7311
|
// src/hevcPreviewLint.ts
|
|
7286
7312
|
import { execFile } from "child_process";
|
|
7287
|
-
import { existsSync } from "fs";
|
|
7288
|
-
import { join } from "path";
|
|
7313
|
+
import { existsSync as existsSync2 } from "fs";
|
|
7314
|
+
import { join as join2 } from "path";
|
|
7289
7315
|
import { rewriteAssetPath } from "@hyperframes/parsers/asset-paths";
|
|
7290
7316
|
import { parseNumeric } from "@hyperframes/parsers/composition-contract";
|
|
7291
7317
|
import { findFfBinary } from "@hyperframes/parsers/ff-binaries";
|
|
@@ -7326,7 +7352,7 @@ function resolveLocalVideoReference(projectDir, rawSrc, compSrcPath) {
|
|
|
7326
7352
|
if (isUnresolvedAssetPlaceholder(rawSrc)) return null;
|
|
7327
7353
|
const src = cleanAssetUrl(rawSrc);
|
|
7328
7354
|
if (!src || isRemoteOrInlineUrl(src)) return null;
|
|
7329
|
-
const rootRelative = compSrcPath ? rewriteAssetPath(compSrcPath, src, (path) =>
|
|
7355
|
+
const rootRelative = compSrcPath ? rewriteAssetPath(compSrcPath, src, (path) => existsSync2(join2(projectDir, path))) : src;
|
|
7330
7356
|
const asset = resolveExistingLocalAsset(projectDir, rootRelative);
|
|
7331
7357
|
return asset ? { resolved: asset.resolved, src } : null;
|
|
7332
7358
|
}
|
|
@@ -7520,7 +7546,7 @@ async function lintHevcPreviewCodec(candidates) {
|
|
|
7520
7546
|
|
|
7521
7547
|
// src/projectShaders.ts
|
|
7522
7548
|
import { realpathSync } from "fs";
|
|
7523
|
-
import { dirname, join as
|
|
7549
|
+
import { dirname, join as join3, relative } from "path";
|
|
7524
7550
|
import {
|
|
7525
7551
|
isWithinProjectRoot,
|
|
7526
7552
|
readProjectFile,
|
|
@@ -7531,7 +7557,7 @@ function isAuthoredScript(path) {
|
|
|
7531
7557
|
}
|
|
7532
7558
|
function localScript(projectDir, src, compSrcPath) {
|
|
7533
7559
|
if (/^(?:[a-z][a-z\d+.-]*:|\/\/|#)/i.test(src)) return;
|
|
7534
|
-
const besideFile = src.startsWith("/") ? src :
|
|
7560
|
+
const besideFile = src.startsWith("/") ? src : join3(dirname(compSrcPath ?? "index.html"), src);
|
|
7535
7561
|
const asset = resolveExistingLocalAsset2(projectDir, besideFile);
|
|
7536
7562
|
if (!asset) return;
|
|
7537
7563
|
try {
|
|
@@ -7604,7 +7630,7 @@ function collectLocalStylesheets(projectDir, document, compSrcPath) {
|
|
|
7604
7630
|
if (!rel.split(/\s+/).some((part) => part.toLowerCase() === "stylesheet")) continue;
|
|
7605
7631
|
const href = link.getAttribute("href") ?? "";
|
|
7606
7632
|
if (!isLocalStylesheetHref(href)) continue;
|
|
7607
|
-
const besideFile = compSrcPath ?
|
|
7633
|
+
const besideFile = compSrcPath ? join4(dirname2(compSrcPath), href) : href;
|
|
7608
7634
|
const stylesheet = resolveExistingLocalAsset3(projectDir, besideFile) ?? (compSrcPath && staysInsideProjectRoot(href) ? resolveExistingLocalAsset3(projectDir, href) : null);
|
|
7609
7635
|
if (!stylesheet) continue;
|
|
7610
7636
|
const read = readProjectFile2(stylesheet.resolved);
|
|
@@ -7621,11 +7647,17 @@ function collectExternalStyles(projectDir, html, compSrcPath) {
|
|
|
7621
7647
|
document,
|
|
7622
7648
|
compSrcPath
|
|
7623
7649
|
)) {
|
|
7624
|
-
styles.push({ href, content, file:
|
|
7650
|
+
styles.push({ href, content, file: join4(projectDir, rootRelativePath), rootRelativePath });
|
|
7625
7651
|
}
|
|
7626
7652
|
return styles;
|
|
7627
7653
|
}
|
|
7628
7654
|
var MAX_EXTERNAL_SCRIPT_BYTES = 1e6;
|
|
7655
|
+
function linkedSources(projectDir, html, compSrcPath) {
|
|
7656
|
+
return {
|
|
7657
|
+
externalStyles: collectExternalStyles(projectDir, html, compSrcPath),
|
|
7658
|
+
externalScripts: collectExternalScripts(projectDir, html, compSrcPath)
|
|
7659
|
+
};
|
|
7660
|
+
}
|
|
7629
7661
|
function collectExternalScripts(projectDir, html, compSrcPath) {
|
|
7630
7662
|
const scripts = [];
|
|
7631
7663
|
const { document } = parseHTML3(html);
|
|
@@ -7633,7 +7665,7 @@ function collectExternalScripts(projectDir, html, compSrcPath) {
|
|
|
7633
7665
|
const src = element.getAttribute("src") ?? "";
|
|
7634
7666
|
if (!isLocalStylesheetHref(src) || /\.min\.m?js(?:[?#]|$)/i.test(src)) continue;
|
|
7635
7667
|
const path = src.split(/[?#]/)[0] ?? src;
|
|
7636
|
-
const besideFile = compSrcPath ?
|
|
7668
|
+
const besideFile = compSrcPath ? join4(dirname2(compSrcPath), path) : path;
|
|
7637
7669
|
const script = resolveExistingLocalAsset3(projectDir, besideFile) ?? (compSrcPath && staysInsideProjectRoot(path) ? resolveExistingLocalAsset3(projectDir, path) : null);
|
|
7638
7670
|
if (!script) continue;
|
|
7639
7671
|
const read = readProjectFile2(script.resolved);
|
|
@@ -7643,7 +7675,7 @@ function collectExternalScripts(projectDir, html, compSrcPath) {
|
|
|
7643
7675
|
src,
|
|
7644
7676
|
attrs,
|
|
7645
7677
|
content: read.text,
|
|
7646
|
-
file:
|
|
7678
|
+
file: join4(projectDir, script.rootRelativePath),
|
|
7647
7679
|
rootRelativePath: script.rootRelativePath
|
|
7648
7680
|
});
|
|
7649
7681
|
}
|
|
@@ -7672,12 +7704,12 @@ function collectCssSources(projectDir, html, compSrcPath) {
|
|
|
7672
7704
|
function resolveCssAssetCandidates(projectDir, url, htmlCompSrcPath, cssRootRelativePath) {
|
|
7673
7705
|
if (url.startsWith("/")) return resolveLocalAssetCandidates(projectDir, url);
|
|
7674
7706
|
if (cssRootRelativePath) {
|
|
7675
|
-
return resolveLocalAssetCandidates(projectDir,
|
|
7707
|
+
return resolveLocalAssetCandidates(projectDir, join4(dirname2(cssRootRelativePath), url));
|
|
7676
7708
|
}
|
|
7677
7709
|
if (htmlCompSrcPath) {
|
|
7678
7710
|
return resolveLocalAssetCandidates(
|
|
7679
7711
|
projectDir,
|
|
7680
|
-
rewriteAssetPath2(htmlCompSrcPath, url, (path) =>
|
|
7712
|
+
rewriteAssetPath2(htmlCompSrcPath, url, (path) => existsSync3(join4(projectDir, path)))
|
|
7681
7713
|
);
|
|
7682
7714
|
}
|
|
7683
7715
|
return resolveLocalAssetCandidates(projectDir, url);
|
|
@@ -7738,9 +7770,8 @@ async function lintProject(projectDir, entryFile, hostOptions = {}) {
|
|
|
7738
7770
|
...hostOptions,
|
|
7739
7771
|
filePath: indexPath,
|
|
7740
7772
|
compSrcPath: rootFile,
|
|
7741
|
-
|
|
7742
|
-
projectFontFamilies
|
|
7743
|
-
externalScripts: collectExternalScripts(projectDir, rootHtml, rootCompSrcPath)
|
|
7773
|
+
...linkedSources(projectDir, rootHtml, rootCompSrcPath),
|
|
7774
|
+
projectFontFamilies
|
|
7744
7775
|
});
|
|
7745
7776
|
results.push({ file: rootFile, result: rootResult, contentHash: contentDigest(rootHtml) });
|
|
7746
7777
|
totalErrors += rootResult.errorCount;
|
|
@@ -7748,14 +7779,16 @@ async function lintProject(projectDir, entryFile, hostOptions = {}) {
|
|
|
7748
7779
|
totalInfos += rootResult.infoCount;
|
|
7749
7780
|
const allHtmlSources = [{ html: rootHtml, compSrcPath: rootCompSrcPath }];
|
|
7750
7781
|
const compositionsDir = resolve(projectDir, "compositions");
|
|
7751
|
-
if (!entryFile &&
|
|
7782
|
+
if (!entryFile && existsSync3(compositionsDir)) {
|
|
7783
|
+
const ignored = projectIgnore(projectDir);
|
|
7752
7784
|
const collectHtmlFiles = (dir, rel) => {
|
|
7753
7785
|
const out = [];
|
|
7754
7786
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
7755
7787
|
const relPath = rel ? `${rel}/${entry.name}` : entry.name;
|
|
7788
|
+
if (ignored.ignores(`compositions/${relPath}${entry.isDirectory() ? "/" : ""}`)) continue;
|
|
7756
7789
|
if (entry.isDirectory()) {
|
|
7757
7790
|
if (!rel && entry.name === "components") continue;
|
|
7758
|
-
out.push(...collectHtmlFiles(
|
|
7791
|
+
out.push(...collectHtmlFiles(join4(dir, entry.name), relPath));
|
|
7759
7792
|
} else if (entry.isFile() && entry.name.endsWith(".html") && !entry.name.startsWith("._")) {
|
|
7760
7793
|
out.push(relPath);
|
|
7761
7794
|
}
|
|
@@ -7764,7 +7797,7 @@ async function lintProject(projectDir, entryFile, hostOptions = {}) {
|
|
|
7764
7797
|
};
|
|
7765
7798
|
const files = collectHtmlFiles(compositionsDir, "").sort();
|
|
7766
7799
|
for (const file of files) {
|
|
7767
|
-
const filePath =
|
|
7800
|
+
const filePath = join4(compositionsDir, file);
|
|
7768
7801
|
const html = readTextFile(filePath);
|
|
7769
7802
|
const compSrcPath = `compositions/${file}`;
|
|
7770
7803
|
allHtmlSources.push({ html, compSrcPath });
|
|
@@ -7774,9 +7807,8 @@ async function lintProject(projectDir, entryFile, hostOptions = {}) {
|
|
|
7774
7807
|
filePath,
|
|
7775
7808
|
isSubComposition: true,
|
|
7776
7809
|
compSrcPath,
|
|
7777
|
-
|
|
7778
|
-
projectFontFamilies
|
|
7779
|
-
externalScripts: collectExternalScripts(projectDir, html, compSrcPath)
|
|
7810
|
+
...linkedSources(projectDir, html, compSrcPath),
|
|
7811
|
+
projectFontFamilies
|
|
7780
7812
|
});
|
|
7781
7813
|
results.push({
|
|
7782
7814
|
file: `compositions/${file}`,
|
|
@@ -7878,7 +7910,7 @@ function lintProjectAudioFiles(projectDir, htmlSources) {
|
|
|
7878
7910
|
function urlSafeSrcOf(projectDir, rootRelative) {
|
|
7879
7911
|
if (!/[?#]/.test(rootRelative)) return null;
|
|
7880
7912
|
const literal = resolve(projectDir, rootRelative.replace(/^\//, ""));
|
|
7881
|
-
if (!
|
|
7913
|
+
if (!existsSync3(literal)) return null;
|
|
7882
7914
|
return encodeUrlPath(rootRelative);
|
|
7883
7915
|
}
|
|
7884
7916
|
var urlSafeHint = (safe) => `The file exists, but a "#" or "?" in a src ends the file name there. Write the src as "${safe}".`;
|
|
@@ -7894,8 +7926,8 @@ function lintAudioSrcNotFound(projectDir, htmlSources) {
|
|
|
7894
7926
|
const src = mediaSrcOf(match);
|
|
7895
7927
|
if (/^(https?:|data:|blob:)/i.test(src)) continue;
|
|
7896
7928
|
if (isUnresolvedAssetPlaceholder2(src)) continue;
|
|
7897
|
-
const rootRelative = compSrcPath ? rewriteAssetPath2(compSrcPath, src, (path) =>
|
|
7898
|
-
if (!
|
|
7929
|
+
const rootRelative = compSrcPath ? rewriteAssetPath2(compSrcPath, src, (path) => existsSync3(join4(projectDir, path))) : src;
|
|
7930
|
+
if (!existsSync3(resolveProjectRelativeSrc(rootRelative, projectDir))) {
|
|
7899
7931
|
const safe = urlSafeSrcOf(projectDir, rootRelative);
|
|
7900
7932
|
if (safe) urlSafe.set(src, safe);
|
|
7901
7933
|
const file = sourceFile(projectDir, compSrcPath);
|
|
@@ -7931,13 +7963,13 @@ function lintMissingLocalAsset(projectDir, htmlSources) {
|
|
|
7931
7963
|
const src = cleanAssetUrl2(rawSrc);
|
|
7932
7964
|
if (!src) continue;
|
|
7933
7965
|
if (isRemoteOrInlineUrl2(src)) continue;
|
|
7934
|
-
const rootRelative = compSrcPath ? rewriteAssetPath2(compSrcPath, src, (path) =>
|
|
7966
|
+
const rootRelative = compSrcPath ? rewriteAssetPath2(compSrcPath, src, (path) => existsSync3(join4(projectDir, path))) : src;
|
|
7935
7967
|
const resolvedAsset = resolveExistingLocalAsset3(projectDir, rootRelative);
|
|
7936
7968
|
if (resolvedAsset) continue;
|
|
7937
7969
|
const rawRoot = rawSrc.trim();
|
|
7938
7970
|
const safe = urlSafeSrcOf(
|
|
7939
7971
|
projectDir,
|
|
7940
|
-
compSrcPath ? rewriteAssetPath2(compSrcPath, rawRoot, (path) =>
|
|
7972
|
+
compSrcPath ? rewriteAssetPath2(compSrcPath, rawRoot, (path) => existsSync3(join4(projectDir, path))) : rawRoot
|
|
7941
7973
|
);
|
|
7942
7974
|
if (safe) urlSafe.set(src, safe);
|
|
7943
7975
|
const resolvedKey = resolve(projectDir, rootRelative);
|
|
@@ -7970,11 +8002,11 @@ function lintTextureMaskAssetNotFound(projectDir, htmlSources) {
|
|
|
7970
8002
|
for (const cssSource of collectCssSources(projectDir, html, compSrcPath)) {
|
|
7971
8003
|
let matches = [];
|
|
7972
8004
|
try {
|
|
7973
|
-
|
|
8005
|
+
postcss5.parse(cssSource.content).walkDecls(/^(?:-webkit-)?mask-image$/i, (declaration) => {
|
|
7974
8006
|
matches.push(...declaration.value.matchAll(CSS_URL_RE));
|
|
7975
8007
|
});
|
|
7976
8008
|
} catch (error) {
|
|
7977
|
-
if (!(error instanceof
|
|
8009
|
+
if (!(error instanceof postcss5.CssSyntaxError)) throw error;
|
|
7978
8010
|
matches = [...cssSource.content.matchAll(MASK_IMAGE_URL_RE)];
|
|
7979
8011
|
}
|
|
7980
8012
|
for (const match of matches) {
|
|
@@ -7988,7 +8020,7 @@ function lintTextureMaskAssetNotFound(projectDir, htmlSources) {
|
|
|
7988
8020
|
compSrcPath,
|
|
7989
8021
|
cssSource.rootRelativePath
|
|
7990
8022
|
);
|
|
7991
|
-
if (candidates.some(
|
|
8023
|
+
if (candidates.some(existsSync3)) continue;
|
|
7992
8024
|
missingByFile.set(file, (missingByFile.get(file) ?? /* @__PURE__ */ new Set()).add(url));
|
|
7993
8025
|
}
|
|
7994
8026
|
}
|
|
@@ -8008,8 +8040,8 @@ function nearestAncestorMatch(projectDir, cssRootRelativePath, url) {
|
|
|
8008
8040
|
const root = resolve(projectDir);
|
|
8009
8041
|
for (let dir = resolve(root, dirname2(cssRootRelativePath), ".."); ; dir = dirname2(dir)) {
|
|
8010
8042
|
if (!isWithinProjectRoot2(projectDir, dir)) return null;
|
|
8011
|
-
if (resolveLocalAssetCandidates(projectDir, relative2(root,
|
|
8012
|
-
return relative2(dirname2(resolve(root, cssRootRelativePath)),
|
|
8043
|
+
if (resolveLocalAssetCandidates(projectDir, relative2(root, join4(dir, url))).some(existsSync3)) {
|
|
8044
|
+
return relative2(dirname2(resolve(root, cssRootRelativePath)), join4(dir, url)).replace(
|
|
8013
8045
|
/\\/g,
|
|
8014
8046
|
"/"
|
|
8015
8047
|
);
|
|
@@ -8028,9 +8060,9 @@ function lintLinkedStylesheetAssets(projectDir, htmlSources) {
|
|
|
8028
8060
|
)) {
|
|
8029
8061
|
let root;
|
|
8030
8062
|
try {
|
|
8031
|
-
root =
|
|
8063
|
+
root = postcss5.parse(content);
|
|
8032
8064
|
} catch (error) {
|
|
8033
|
-
if (!(error instanceof
|
|
8065
|
+
if (!(error instanceof postcss5.CssSyntaxError)) throw error;
|
|
8034
8066
|
continue;
|
|
8035
8067
|
}
|
|
8036
8068
|
root.walkDecls((declaration) => {
|
|
@@ -8040,7 +8072,7 @@ function lintLinkedStylesheetAssets(projectDir, htmlSources) {
|
|
|
8040
8072
|
if (isUnresolvedAssetPlaceholder2(rawUrl)) continue;
|
|
8041
8073
|
const url = cleanAssetUrl2(rawUrl);
|
|
8042
8074
|
if (!url || isRemoteOrInlineUrl2(url) || url.startsWith("/")) continue;
|
|
8043
|
-
if (resolveCssAssetCandidates(projectDir, url, void 0, rootRelativePath).some(
|
|
8075
|
+
if (resolveCssAssetCandidates(projectDir, url, void 0, rootRelativePath).some(existsSync3))
|
|
8044
8076
|
continue;
|
|
8045
8077
|
const key = `${rootRelativePath}\0${url}`;
|
|
8046
8078
|
if (seen.has(key)) continue;
|
|
@@ -8049,7 +8081,7 @@ function lintLinkedStylesheetAssets(projectDir, htmlSources) {
|
|
|
8049
8081
|
findings.push({
|
|
8050
8082
|
code: "missing_stylesheet_asset",
|
|
8051
8083
|
severity: "warning",
|
|
8052
|
-
file:
|
|
8084
|
+
file: join4(projectDir, rootRelativePath),
|
|
8053
8085
|
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.`,
|
|
8054
8086
|
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.`
|
|
8055
8087
|
});
|
|
@@ -8069,7 +8101,7 @@ function lintMultipleRootCompositions(projectDir) {
|
|
|
8069
8101
|
const rootCompositions = [];
|
|
8070
8102
|
for (const file of rootHtmlFiles) {
|
|
8071
8103
|
if (PAGES_OPENED_BESIDE_INDEX.has(file)) continue;
|
|
8072
|
-
const content = readTextFile(
|
|
8104
|
+
const content = readTextFile(join4(projectDir, file));
|
|
8073
8105
|
if (/data-composition-id/i.test(content)) {
|
|
8074
8106
|
rootCompositions.push(file);
|
|
8075
8107
|
}
|
|
@@ -8234,9 +8266,10 @@ export {
|
|
|
8234
8266
|
LINT_RULE_COUNT,
|
|
8235
8267
|
LINT_RULE_GROUP_COUNTS,
|
|
8236
8268
|
audioAttributeShapeError,
|
|
8237
|
-
|
|
8269
|
+
linkedSources,
|
|
8238
8270
|
lintHyperframeHtml,
|
|
8239
8271
|
lintMediaUrls,
|
|
8240
8272
|
lintProject,
|
|
8273
|
+
projectIgnore,
|
|
8241
8274
|
shouldBlockRender
|
|
8242
8275
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/lint",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.18",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/heygen-com/hyperframes",
|
|
@@ -32,10 +32,11 @@
|
|
|
32
32
|
"acorn": "^8.17.0",
|
|
33
33
|
"acorn-walk": "^8.3.5",
|
|
34
34
|
"htmlparser2": "^10.1.0",
|
|
35
|
+
"ignore": "^5.3.2",
|
|
35
36
|
"linkedom": "^0.18.12",
|
|
36
37
|
"postcss": "^8.5.8",
|
|
37
38
|
"postcss-selector-parser": "^7.1.4",
|
|
38
|
-
"@hyperframes/parsers": "0.9.
|
|
39
|
+
"@hyperframes/parsers": "0.9.18"
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
|
41
42
|
"@types/node": "^25.0.10",
|