(opts: RunPreviewOptions)
| 52 | ] as const; |
| 53 | |
| 54 | export async function runPreview(opts: RunPreviewOptions): Promise<PreviewResult> { |
| 55 | const absWorkspace = resolve(opts.workspaceRoot); |
| 56 | let source: string; |
| 57 | let sourcePath = opts.path; |
| 58 | try { |
| 59 | source = await readPreviewSource(absWorkspace, opts.path); |
| 60 | if (isHtmlPreviewPath(opts.path)) { |
| 61 | const reference = findArtifactSourceReference(source); |
| 62 | const referencedPath = |
| 63 | reference === null ? null : resolveArtifactSourceReferencePath(opts.path, reference); |
| 64 | if (referencedPath !== null) { |
| 65 | source = await readPreviewSource(absWorkspace, referencedPath); |
| 66 | sourcePath = referencedPath; |
| 67 | } |
| 68 | } |
| 69 | } catch (err) { |
| 70 | return emptyFail(err instanceof Error ? err.message : String(err)); |
| 71 | } |
| 72 | |
| 73 | let html: string; |
| 74 | try { |
| 75 | html = buildPreviewDocument(source, { |
| 76 | path: sourcePath, |
| 77 | baseHref: pathToFileURL(absWorkspace.endsWith(sep) ? absWorkspace : `${absWorkspace}${sep}`) |
| 78 | .href, |
| 79 | }); |
| 80 | } catch (err) { |
| 81 | return emptyFail(err instanceof Error ? err.message : String(err)); |
| 82 | } |
| 83 | |
| 84 | let executablePath: string; |
| 85 | try { |
| 86 | executablePath = await findSystemChrome(); |
| 87 | } catch (err) { |
| 88 | return emptyFail( |
| 89 | `system Chrome unavailable: ${err instanceof Error ? err.message : String(err)}`, |
| 90 | ); |
| 91 | } |
| 92 | |
| 93 | const puppeteer = (await import('puppeteer-core')).default; |
| 94 | |
| 95 | const consoleErrors: PreviewResult['consoleErrors'] = []; |
| 96 | const assetErrors: PreviewResult['assetErrors'] = []; |
| 97 | const ignoreOptionalRuntimeFontFailures = previewIncludesRuntimeFontLinks(html); |
| 98 | const startTs = Date.now(); |
| 99 | let browser: Browser | null = null; |
| 100 | let page: Page | null = null; |
| 101 | // Launch with an isolated, disposable user-data-dir. Without this puppeteer |
| 102 | // tries to reuse the user's default Chrome profile; macOS's single-instance |
| 103 | // handling then activates their running Chrome (bouncing the Dock icon) |
| 104 | // instead of starting a headless worker. A per-call tmpdir plus |
| 105 | // --headless=new keeps the launch invisible AND independent of whatever |
| 106 | // Chrome windows the user has open. |
| 107 | const userDataDir = await mkdtemp(join(tmpdir(), 'codesign-preview-')); |
| 108 | try { |
| 109 | browser = await puppeteer.launch({ |
| 110 | executablePath, |
| 111 | headless: true, |
no test coverage detected