Files
backnotprop__plannotator/apps/pi-extension/plannotator-browser-runtime.ts
Devinda Senanayaka 929e8f9f59 perf(pi-extension): use exact .ts relative specifiers (#1106)
The Pi extension is distributed and executed as raw TypeScript through
Pi's jiti loader. Phantom .js paths force jiti through its last-resort
fallback, while extensionless paths still require probing. Exact .ts
paths avoid both and match the files that actually ship.

- rewrite relative .js and extensionless specifiers in hand-written
  sources (the existing noEmit bundler tsconfig permits .ts imports)
- make vendor.sh emit .ts in explicit rewrite rules and normalize both
  source styles across verbatim-copied generated modules
- guard hand-written and generated sources against either inexact form

Extension module import: 43.5ms -> 15ms median (PI_TIMING, n=12
interleaved). bun test: 73/73 (pi-extension), 456/456 (server); all
seven TypeScript project checks and the packed-artifact audit pass.

Co-authored-by: dca123 <dca123@users.noreply.github.com>
Co-authored-by: Michael Ramos <mdramos8@gmail.com>
2026-07-22 16:36:17 -07:00

78 lines
2.6 KiB
TypeScript

import { readFileSync, statSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
type PlannotatorBrowserModule = typeof import("./plannotator-browser.ts");
const moduleDirectory = dirname(fileURLToPath(import.meta.url));
const planHtmlPath = resolve(moduleDirectory, "plannotator.html");
const reviewHtmlPath = resolve(moduleDirectory, "review-editor.html");
let browserModulePromise: Promise<PlannotatorBrowserModule> | undefined;
let planHtmlContent: string | undefined;
let reviewHtmlContent: string | undefined;
function hasReadableAsset(path: string, cachedContent: string | undefined): boolean {
if (cachedContent) return true;
try {
const stats = statSync(path);
return stats.isFile() && stats.size > 0;
} catch {
return false;
}
}
function readBrowserAsset(path: string, cachedContent: string | undefined): string {
if (cachedContent !== undefined) return cachedContent;
try {
return readFileSync(path, "utf-8");
} catch {
return "";
}
}
/** Return whether the built plan/annotation/archive UI is available without reading it into memory. */
export function hasPlanBrowserHtml(): boolean {
return hasReadableAsset(planHtmlPath, planHtmlContent);
}
/** Return whether the built code-review UI is available without reading it into memory. */
export function hasReviewBrowserHtml(): boolean {
return hasReadableAsset(reviewHtmlPath, reviewHtmlContent);
}
/** Read and cache the built plan/annotation/archive UI on first use. */
export function getPlanBrowserHtml(): string {
const content = readBrowserAsset(planHtmlPath, planHtmlContent);
if (content) planHtmlContent = content;
return content;
}
/** Read and cache the built code-review UI on first use. */
export function getReviewBrowserHtml(): string {
const content = readBrowserAsset(reviewHtmlPath, reviewHtmlContent);
if (content) reviewHtmlContent = content;
return content;
}
/** Convert a startup failure into the stable user-facing message used by Pi commands and events. */
export function getStartupErrorMessage(error: unknown): string {
return error instanceof Error ? error.message : "Unknown error";
}
/**
* Load the browser/server graph on first use.
*
* Concurrent callers share one import, while a failed import is cleared so a
* later invocation can retry instead of retaining a permanently rejected promise.
*/
export function loadPlannotatorBrowser(): Promise<PlannotatorBrowserModule> {
if (!browserModulePromise) {
browserModulePromise = import("./plannotator-browser.ts").catch((error: unknown) => {
browserModulePromise = undefined;
throw error;
});
}
return browserModulePromise;
}