Ship file viewer, editor improvements, and private package releases
This commit is contained in:
1 parent
645914955c
commit
a4a31032b9
158 files changed
+10523
-137
No files matched your search
@@ -0,0 +1,106 @@
|
||||
import { createRequire } from "node:module";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { definePlugin } from "@wrnexus/plugin";
|
||||
import { configureConversion } from "./convert/config.ts";
|
||||
import type { ConversionOptions } from "./convert/types.ts";
|
||||
import { configureUploads } from "./uploads/config.ts";
|
||||
import type { ViewerUploadsOptions } from "./uploads/types.ts";
|
||||
|
||||
export interface ViewerPluginOptions {
|
||||
/**
|
||||
* Server-side conversion for formats no browser can render. Omitted, the
|
||||
* plugin mounts no routes and never runs an external process; the affected
|
||||
* formats keep their download card.
|
||||
*/
|
||||
convert?: ConversionOptions;
|
||||
/**
|
||||
* Lets `<Viewer key="…">` resolve `@wrnexus/uploader` storage keys. Omitted,
|
||||
* no routes are mounted and a `key` on its own produces the download card.
|
||||
*/
|
||||
uploads?: ViewerUploadsOptions;
|
||||
}
|
||||
|
||||
const here = (path: string) =>
|
||||
fileURLToPath(
|
||||
new URL(import.meta.url.endsWith(".js") ? path.replace(/\.ts$/, ".js") : path, import.meta.url),
|
||||
);
|
||||
|
||||
const required = createRequire(import.meta.url);
|
||||
function resolves(specifier: string): string | undefined {
|
||||
try {
|
||||
return required.resolve(specifier);
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tier 2 parsers with an optional peer dependency. Each is served as its own
|
||||
* runtime asset and contributed only when its package resolves here, so an app
|
||||
* that never installs `xlsx` neither builds nor ships it — and the matching
|
||||
* renderer reports itself unavailable because the asset is not there.
|
||||
*/
|
||||
const OPTIONAL = [
|
||||
{ id: "viewer-sheet", package: "xlsx", entry: "./optional/sheet.ts" },
|
||||
{ id: "viewer-docx", package: "mammoth", entry: "./optional/docx.ts" },
|
||||
{ id: "viewer-zip", package: "fflate", entry: "./optional/zip.ts" },
|
||||
{ id: "viewer-office", package: "fflate", entry: "./optional/office.ts" },
|
||||
{ id: "viewer-pdfjs", package: "pdfjs-dist", entry: "./optional/pdf.ts" },
|
||||
];
|
||||
|
||||
function optionalRuntimes(): { id: string; entry: string; bundle?: boolean }[] {
|
||||
const runtimes: { id: string; entry: string; bundle?: boolean }[] = [];
|
||||
for (const item of OPTIONAL) {
|
||||
if (!resolves(item.package)) continue;
|
||||
runtimes.push({ id: item.id, entry: here(item.entry) });
|
||||
}
|
||||
// pdf.js is served from its own published build, not bundled: putting its ESM
|
||||
// entry through a bundler breaks the worker handshake. Both files are already
|
||||
// browser-ready ESM, and `.mjs` is served verbatim.
|
||||
const library = resolves("pdfjs-dist/build/pdf.mjs");
|
||||
const worker = resolves("pdfjs-dist/build/pdf.worker.mjs");
|
||||
if (library && worker && runtimes.some((runtime) => runtime.id === "viewer-pdfjs")) {
|
||||
runtimes.push({ id: "viewer-pdfjs-lib", entry: library, bundle: false });
|
||||
runtimes.push({ id: "viewer-pdfjs-worker", entry: worker, bundle: false });
|
||||
}
|
||||
return runtimes;
|
||||
}
|
||||
|
||||
const route = (path: string, entry: string) => ({
|
||||
kind: "api" as const,
|
||||
path: `/api/viewer/${path}`,
|
||||
entry: here(`./routes/${entry}.ts`),
|
||||
});
|
||||
|
||||
function routes(options: ViewerPluginOptions) {
|
||||
return [
|
||||
...(options.convert
|
||||
? [
|
||||
route("capabilities", "capabilities"),
|
||||
route("preview", "preview"),
|
||||
route("thumb", "thumb"),
|
||||
route("text", "text"),
|
||||
]
|
||||
: []),
|
||||
...(options.uploads ? [route("source", "source"), route("file", "file")] : []),
|
||||
];
|
||||
}
|
||||
|
||||
export function viewerPlugin(options: ViewerPluginOptions = {}) {
|
||||
configureConversion(options.convert);
|
||||
configureUploads(options.uploads);
|
||||
return definePlugin({
|
||||
name: "@wrnexus/viewer",
|
||||
componentDirs: [here("../components")],
|
||||
clientRuntimes: [
|
||||
{ id: "viewer-code", entry: here("./code-editor.ts") },
|
||||
{ id: "viewer", entry: here("./runtime.ts") },
|
||||
...optionalRuntimes(),
|
||||
],
|
||||
styleSources: [{ id: "viewer", entry: here("../viewer.css") }],
|
||||
// Each feature mounts only its own routes, so enabling one does not publish
|
||||
// endpoints for the other that could only answer "not enabled".
|
||||
...(routes(options).length ? { routeEntries: routes(options) } : {}),
|
||||
});
|
||||
}
|
||||
export default viewerPlugin;
|
||||
Reference in new issue
Block a user