( projectDir: string, htmlSources: HtmlSource[], )
| 286 | } |
| 287 | |
| 288 | function lintAudioSrcNotFound( |
| 289 | projectDir: string, |
| 290 | htmlSources: HtmlSource[], |
| 291 | ): HyperframeLintFinding[] { |
| 292 | const findings: HyperframeLintFinding[] = []; |
| 293 | |
| 294 | const audioSrcRe = /<audio\b[^>]*\bsrc\s*=\s*["']([^"']+)["'][^>]*>/gi; |
| 295 | |
| 296 | const missingSrcs: string[] = []; |
| 297 | for (const { html, compSrcPath } of htmlSources) { |
| 298 | let match: RegExpExecArray | null; |
| 299 | while ((match = audioSrcRe.exec(html)) !== null) { |
| 300 | const src = match[1]!; |
| 301 | if (/^(https?:|data:|blob:)/i.test(src)) continue; |
| 302 | if (/^__[A-Z_]+__$/.test(src)) continue; |
| 303 | const rootRelative = compSrcPath ? rewriteAssetPath(compSrcPath, src) : src; |
| 304 | if (!resolveLocalAssetCandidates(projectDir, rootRelative).some(existsSync)) { |
| 305 | missingSrcs.push(src); |
| 306 | } |
| 307 | } |
| 308 | } |
| 309 | |
| 310 | if (missingSrcs.length > 0) { |
| 311 | const unique = [...new Set(missingSrcs)]; |
| 312 | findings.push({ |
| 313 | code: "audio_src_not_found", |
| 314 | severity: "error", |
| 315 | message: `<audio> element references file(s) not found in the project: ${unique.join(", ")}. The rendered video will be silent.`, |
| 316 | fixHint: |
| 317 | unique.length === 1 |
| 318 | ? `Add the file "${unique[0]}" to the project directory, or update the src attribute to point to an existing file.` |
| 319 | : `Add the missing files to the project directory, or update the src attributes to point to existing files.`, |
| 320 | }); |
| 321 | } |
| 322 | |
| 323 | return findings; |
| 324 | } |
| 325 | |
| 326 | function maskRange(src: string, pattern: RegExp): string { |
| 327 | return src.replace(pattern, (m) => " ".repeat(m.length)); |
no test coverage detected