( projectDir: string, htmlSources: HtmlSource[], )
| 392 | } |
| 393 | |
| 394 | function lintTextureMaskAssetNotFound( |
| 395 | projectDir: string, |
| 396 | htmlSources: HtmlSource[], |
| 397 | ): HyperframeLintFinding[] { |
| 398 | const missing = new Map<string, string>(); |
| 399 | |
| 400 | for (const { html, compSrcPath } of htmlSources) { |
| 401 | for (const cssSource of collectCssSources(projectDir, html, compSrcPath)) { |
| 402 | let match: RegExpExecArray | null; |
| 403 | const pattern = new RegExp(MASK_IMAGE_URL_RE.source, MASK_IMAGE_URL_RE.flags); |
| 404 | while ((match = pattern.exec(cssSource.content)) !== null) { |
| 405 | const rawUrl = match[1] ?? match[2] ?? match[3] ?? ""; |
| 406 | const url = cleanAssetUrl(rawUrl); |
| 407 | if (!url || isRemoteOrInlineUrl(url)) continue; |
| 408 | if (/^__[A-Z_]+__$/.test(url)) continue; |
| 409 | |
| 410 | const candidates = resolveCssAssetCandidates( |
| 411 | projectDir, |
| 412 | url, |
| 413 | compSrcPath, |
| 414 | cssSource.rootRelativePath, |
| 415 | ); |
| 416 | if (candidates.some(existsSync)) continue; |
| 417 | missing.set(url, candidates[0] ?? resolve(projectDir, url)); |
| 418 | } |
| 419 | } |
| 420 | } |
| 421 | |
| 422 | if (missing.size === 0) return []; |
| 423 | const urls = [...missing.keys()]; |
| 424 | return [ |
| 425 | { |
| 426 | code: "texture_mask_asset_not_found", |
| 427 | severity: "error", |
| 428 | message: `CSS mask-image references file(s) not found in the project: ${urls.join(", ")}.`, |
| 429 | fixHint: |
| 430 | urls.length === 1 |
| 431 | ? `Add "${urls[0]}" to the project, or update the mask-image URL to point to an existing texture mask.` |
| 432 | : "Add the missing texture mask files to the project, or update the mask-image URLs to point to existing files.", |
| 433 | }, |
| 434 | ]; |
| 435 | } |
| 436 | |
| 437 | function lintMultipleRootCompositions(projectDir: string): HyperframeLintFinding[] { |
| 438 | const findings: HyperframeLintFinding[] = []; |
no test coverage detected