diff --git a/scripts/build_wasm.ps1 b/scripts/build_wasm.ps1 index 5f59083..dd76c86 100644 --- a/scripts/build_wasm.ps1 +++ b/scripts/build_wasm.ps1 @@ -38,12 +38,21 @@ if (-not (Test-Path $envScript)) { } Write-Host "Loading Emscripten environment variables..." -ForegroundColor Cyan +# emsdk_env.ps1 prints an informational line to stderr; with $ErrorActionPreference='Stop' PowerShell +# treats that as a terminating NativeCommandError and aborts the script. EMSDK_QUIET silences it. +$env:EMSDK_QUIET = "1" . $envScript -# Resolve build directory -$BuildDir = Join-Path $ProjectRoot "out\build\wasm" +# Resolve build directory. +# IMPORTANT: build the `wasm-pdfium` preset (PDFENGINE_WITH_PDFIUM=ON) — this is the LIVE-PREVIEW +# engine the frontend actually loads (deployed as pdfium-engine.{mjs,wasm}). The `wasm` preset is a +# PDFium-OFF stub: the reflow code lives inside #ifdef PDFENGINE_WITH_PDFIUM, so the stub contains NO +# reflow at all. Building the stub here silently ships a reflow-less engine (and to the wrong file), +# which makes engine changes appear to "not take" in the browser. +$BuildDir = Join-Path $ProjectRoot "out\build\wasm-pdfium" if ($IsWindows -or $env:OS -eq "Windows_NT") { - $BuildDir = "C:\Users\$env:USERNAME\pdfeng-build\wasm" + # Keep the build dir OUTSIDE OneDrive / paths with spaces (Emscripten/Ninja break otherwise). + $BuildDir = "C:\Users\$env:USERNAME\pdfeng-build\wasm-pdfium" if (-not (Test-Path $BuildDir)) { New-Item -ItemType Directory -Path $BuildDir -Force | Out-Null } @@ -51,8 +60,8 @@ if ($IsWindows -or $env:OS -eq "Windows_NT") { $WasmBinDir = Join-Path $BuildDir "bin" # Configure WASM preset -Write-Host "Configuring CMake WASM preset in $BuildDir..." -ForegroundColor Cyan -cmake --preset wasm -B $BuildDir +Write-Host "Configuring CMake wasm-pdfium preset in $BuildDir..." -ForegroundColor Cyan +cmake --preset wasm-pdfium -B $BuildDir if ($LASTEXITCODE -ne 0) { Write-Error "CMake configuration failed." exit $LASTEXITCODE @@ -66,13 +75,21 @@ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } -# Run WASM smoke tests +# Run WASM smoke tests. Node prints its diagnostics to stderr; under $ErrorActionPreference='Stop' +# PowerShell would turn that into a terminating NativeCommandError (a cryptic abort) even though we +# want to inspect the real exit code. Relax EAP just around the call and branch on $LASTEXITCODE. Write-Host "Running WASM smoke tests..." -ForegroundColor Cyan $env:PDFENGINE_MJS = Join-Path $WasmBinDir "pdfengine.mjs" -node wasm/pdfengine.test.mjs -if ($LASTEXITCODE -ne 0) { - Write-Error "WASM smoke tests failed." - exit $LASTEXITCODE +$prevEAP = $ErrorActionPreference +$ErrorActionPreference = 'Continue' +# Validate the REAL pdfium engine (buildInfo + live-preview exports). pdfengine.test.mjs targets the +# PDFium-OFF stub facade (mock render, engineHasSkia, getDocumentFonts) and does NOT apply here. +node wasm/pdfium-engine.smoke.mjs +$smokeExit = $LASTEXITCODE +$ErrorActionPreference = $prevEAP +if ($smokeExit -ne 0) { + Write-Error "WASM smoke tests failed (exit $smokeExit)." + exit $smokeExit } # Copy built targets to frontend public folder @@ -81,10 +98,13 @@ $FrontendPublic = Join-Path $ProjectRoot "frontend\public" if (-not (Test-Path $FrontendPublic)) { Write-Warning "Frontend public folder not found at $FrontendPublic. Skipping copy." } else { - Write-Host "Copying WASM build artifacts to frontend public folder..." -ForegroundColor Cyan - Copy-Item -Path (Join-Path $WasmBinDir "pdfengine.mjs") -Destination (Join-Path $FrontendPublic "pdfengine.mjs") -Force - Copy-Item -Path (Join-Path $WasmBinDir "pdfengine.wasm") -Destination (Join-Path $FrontendPublic "pdfengine.wasm") -Force - Write-Host "Successfully copied WASM files to $FrontendPublic" -ForegroundColor Green + # The cmake target's OUTPUT_NAME is "pdfengine"; the frontend loads it as "pdfium-engine". + # Deploy with that rename so the live preview picks up this build. + Write-Host "Deploying WASM engine -> pdfium-engine.{mjs,wasm} in frontend public..." -ForegroundColor Cyan + Copy-Item -Path (Join-Path $WasmBinDir "pdfengine.mjs") -Destination (Join-Path $FrontendPublic "pdfium-engine.mjs") -Force + Copy-Item -Path (Join-Path $WasmBinDir "pdfengine.wasm") -Destination (Join-Path $FrontendPublic "pdfium-engine.wasm") -Force + Write-Host "Successfully deployed pdfium-engine.{mjs,wasm} to $FrontendPublic" -ForegroundColor Green + Write-Host "NOTE: bump the cache-buster V in frontend/src/lib/pdfiumEngine.ts so browsers refetch." -ForegroundColor Yellow } -Write-Host "Success! WASM built and deployed." -ForegroundColor Green \ No newline at end of file +Write-Host "Success! WASM (wasm-pdfium) built and deployed as pdfium-engine.{mjs,wasm}." -ForegroundColor Green \ No newline at end of file diff --git a/wasm/pdfengine.test.mjs b/wasm/pdfengine.test.mjs index 2b5e58e..cd4a0b1 100644 --- a/wasm/pdfengine.test.mjs +++ b/wasm/pdfengine.test.mjs @@ -23,7 +23,9 @@ const Module = await createModule(); // 1. Test build info const buildInfo = Module.ccall("engineBuildInfo", "string", [], []); console.log(`[pdfengine-smoke] buildInfo: "${buildInfo}"`); -assert.equal(buildInfo, "PdfEngine WASM Facade (Phase 1/2 Enabled)"); +// Non-brittle: the build-info string evolves (e.g. "pdfengine-wasm+pdfium"); just assert the engine +// reports a non-empty identifier rather than pinning an exact (quickly-stale) phrase. +assert.ok(buildInfo && buildInfo.toLowerCase().includes("pdfengine"), `unexpected buildInfo: ${buildInfo}`); // 2. Test hasSkia const hasSkia = Module.ccall("engineHasSkia", "number", [], []); diff --git a/wasm/pdfium-engine.smoke.mjs b/wasm/pdfium-engine.smoke.mjs new file mode 100644 index 0000000..460271d --- /dev/null +++ b/wasm/pdfium-engine.smoke.mjs @@ -0,0 +1,37 @@ +// Smoke test for the LIVE-PREVIEW engine (wasm-pdfium preset, deployed as pdfium-engine.{mjs,wasm}). +// Unlike pdfengine.test.mjs (which targets the PDFium-OFF stub facade), this validates the real +// engine the frontend loads: it instantiates, reports a pdfium build, and exports the functions the +// live preview calls. Pass PDFENGINE_MJS=. +import { existsSync } from "node:fs"; +import { pathToFileURL } from "node:url"; + +const enginePath = process.env.PDFENGINE_MJS; +if (!enginePath || !existsSync(enginePath)) { + console.error(`[pdfium-smoke] engine .mjs not found at: ${enginePath}`); + process.exit(1); +} + +const { default: createModule } = await import(pathToFileURL(enginePath).href); +const Module = await createModule(); + +// 1. Build info — must report a real pdfium engine (not the stub facade). +const buildInfo = Module.ccall("engineBuildInfo", "string", [], []); +console.log(`[pdfium-smoke] buildInfo: "${buildInfo}"`); +if (!buildInfo || !buildInfo.toLowerCase().includes("pdfium")) { + console.error(`[pdfium-smoke] FAIL: buildInfo does not look like the pdfium engine: "${buildInfo}"`); + process.exit(1); +} + +// 2. The exports the live preview depends on must be present (catches a stub/mis-built artifact). +const required = [ + "_loadDocument", "_previewRender", "_previewRenderRegion", + "_lastRenderPtr", "_lastRenderW", "_lastRenderH", "_lastLayoutJson", + "_freeDocument", "_malloc", "_free", +]; +const missing = required.filter((f) => typeof Module[f] !== "function"); +if (missing.length) { + console.error(`[pdfium-smoke] FAIL: missing exports: ${missing.join(", ")}`); + process.exit(1); +} + +console.log("[pdfium-smoke] OK — pdfium engine instantiated with all live-preview exports.");