mirror of
https://github.com/mudler/LocalAI.git
synced 2026-05-30 11:36:31 -04:00
* Curate the highlight.js build to ~29 languages (lib/core + the common set) instead of the full ~190-grammar default: -787 KB raw / -230 KB gz on the base bundle. * Code-split every route via React.lazy with a per-layout <Suspense> in App.jsx so the sidebar stays mounted on navigation. Initial entry chunk drops from 3194 KB raw / 887 KB gz to 397 KB / 122 KB (-87%). Warm chunks on sidebar hover/focus/touch via a preload registry so the click finds the chunk already in flight or cached. * Migrate Playwright coverage from istanbul (build-time counters) to native Chromium V8 coverage, with per-worker accumulation + conversion. Suite drops from 71s to 30s at 20 workers (~58%) at the non-instrumented floor. * Keep the coverage gate bundling-invariant: the coverage build inlines dynamic imports so every shipped source file lands in the denominator (otherwise untested page chunks silently drop out and inflate the percentage). Production builds stay code-split. * Add UI_TEST_WORKERS=N Makefile knob; tighten coverage tolerance to 0.8pp now that jitter sits near istanbul's ~0.5pp again. Assisted-by: Claude:claude-opus-4-7 [Claude Code] Signed-off-by: Richard Palethorpe <io@richiejp.com>
89 lines
3.9 KiB
JavaScript
89 lines
3.9 KiB
JavaScript
// V8 -> istanbul coverage harvest for the Playwright suite.
|
|
//
|
|
// When PW_V8_COVERAGE=1 the suite runs against a NON-instrumented build (built
|
|
// with COVERAGE_V8=true, which only adds source maps). Chromium collects native
|
|
// V8 coverage with near-zero runtime overhead; we convert it back to per-source
|
|
// istanbul data via v8-to-istanbul (using the on-disk source maps), filter to
|
|
// src/**, and write the same .nyc_output/*.json the istanbul path produced — so
|
|
// `nyc report` and the strict baseline gate are unchanged.
|
|
//
|
|
// Conversion (v8-to-istanbul load() parses the large bundle source map) is the
|
|
// expensive part, so we do NOT convert per test. Instead each worker collects
|
|
// raw V8 coverage from every test, merges it with @bcoe/v8-coverage (which sums
|
|
// counts and reconciles overlapping ranges correctly — applyCoverage can't be
|
|
// called repeatedly, it pushes/overwrites), and converts ONCE at worker
|
|
// teardown. That cuts conversions from ~152 (per test) to ~1 per worker.
|
|
import v8toIstanbul from 'v8-to-istanbul'
|
|
import libCoverage from 'istanbul-lib-coverage'
|
|
import { mergeProcessCovs } from '@bcoe/v8-coverage'
|
|
import { mkdirSync, writeFileSync, existsSync } from 'node:fs'
|
|
import { randomUUID } from 'node:crypto'
|
|
import path from 'node:path'
|
|
|
|
const COVERAGE_DIR = path.resolve(process.cwd(), '.nyc_output')
|
|
const DIST_ASSETS = path.resolve(process.cwd(), 'dist', 'assets')
|
|
// Absolute app source dir. Match on this (not a bare "/src/" substring) — the
|
|
// repo itself lives under .../go/src/..., so a substring check would collide.
|
|
const SRC_DIR = path.resolve(process.cwd(), 'src') + path.sep
|
|
// Only our own bundle chunks under /assets/*.js carry app source maps.
|
|
const APP_CHUNK = /\/assets\/([^/?]+\.js)(\?|$)/
|
|
|
|
export async function startV8(page) {
|
|
// resetOnNavigation:false so hard navigations (goto) within a test accumulate.
|
|
await page.coverage.startJSCoverage({ resetOnNavigation: false })
|
|
}
|
|
|
|
// One accumulator per worker (created by the worker-scoped fixture).
|
|
export function createAccumulator() {
|
|
const processCovs = []
|
|
|
|
return {
|
|
// Called on each test teardown with that test's V8 coverage entries.
|
|
add(entries) {
|
|
const result = entries
|
|
.filter((e) => APP_CHUNK.test(e.url))
|
|
// Keep only structural fields (drop the ~1MB `source` per entry — it's
|
|
// re-read from disk at convert time — to bound per-worker memory).
|
|
.map((e) => ({ scriptId: e.scriptId || e.url, url: e.url, functions: e.functions }))
|
|
if (result.length) processCovs.push({ result })
|
|
},
|
|
|
|
// Called once at worker teardown: merge all tests' coverage, convert, write.
|
|
async flush() {
|
|
if (processCovs.length === 0) return
|
|
const merged = mergeProcessCovs(processCovs)
|
|
const map = libCoverage.createCoverageMap({})
|
|
|
|
for (const script of merged.result) {
|
|
const m = APP_CHUNK.exec(script.url)
|
|
if (!m) continue
|
|
const diskPath = path.join(DIST_ASSETS, m[1])
|
|
if (!existsSync(diskPath)) continue
|
|
|
|
// v8-to-istanbul auto-loads source + sibling .map from disk; the served
|
|
// bytes match dist, so the V8 ranges line up.
|
|
const converter = v8toIstanbul(diskPath, 0)
|
|
try {
|
|
await converter.load()
|
|
converter.applyCoverage(script.functions)
|
|
const data = converter.toIstanbul()
|
|
for (const [key, fileCov] of Object.entries(data)) {
|
|
// v8-to-istanbul keys are already absolute; keep only app sources.
|
|
if (!key.startsWith(SRC_DIR) || key.includes(`${path.sep}node_modules${path.sep}`)) continue
|
|
map.merge({ [key]: fileCov })
|
|
}
|
|
} catch {
|
|
// skip a chunk we couldn't convert
|
|
} finally {
|
|
converter.destroy()
|
|
}
|
|
}
|
|
|
|
const json = map.toJSON()
|
|
if (Object.keys(json).length === 0) return
|
|
mkdirSync(COVERAGE_DIR, { recursive: true })
|
|
writeFileSync(path.join(COVERAGE_DIR, `v8-${randomUUID()}.json`), JSON.stringify(json))
|
|
},
|
|
}
|
|
}
|