aboutsummaryrefslogtreecommitdiff
path: root/src/util/sharp-shim.ts
blob: 7ea409df7103e663d331f704a50538bb9ad54cd7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// Typed minimal shim for the optional `sharp` dependency. Sharp is a heavy
// native module not installed in every workspace; we import dynamically and
// distinguish "missing" (no install) from "error" (installed but threw).
//
// We declare only the surface we use. The dynamic import returns `unknown`
// at the boundary; the cast inside loadSharp() is the documented escape
// hatch — every other consumer sees the typed SharpFactory.

export type SharpInstance = {
  metadata(): Promise<{ width?: number; height?: number }>;
  composite(layers: ReadonlyArray<{ input: Buffer; top: number; left: number }>): SharpInstance;
  resize(width: number, height: number, opts?: { fit?: "inside" }): SharpInstance;
  toFile(path: string): Promise<unknown>;
};

export type SharpFactory = (input: string | Buffer) => SharpInstance;

export type SharpLoad =
  | { readonly kind: "ok"; readonly sharp: SharpFactory }
  | { readonly kind: "missing" }
  | { readonly kind: "error"; readonly message: string };

export const loadSharp = async (): Promise<SharpLoad> => {
  let mod: unknown;
  try {
    // sharp is an optional peer dependency; it may not be installed.
    // @ts-expect-error sharp is an optional peer dependency not required at compile time
    mod = await import("sharp");
  } catch (e) {
    const msg = e instanceof Error ? e.message : String(e);
    if (
      msg.includes("Cannot find module") ||
      msg.includes("MODULE_NOT_FOUND") ||
      msg.includes("ERR_MODULE_NOT_FOUND")
    ) {
      return { kind: "missing" };
    }
    return { kind: "error", message: msg };
  }
  if (mod === null || mod === undefined) return { kind: "missing" };
  // The CommonJS sharp module's default export IS the factory. ESM-wrapped
  // sharp puts it on .default. Probe both.
  const m = mod as { default?: unknown };
  const candidate: unknown = typeof m.default === "function" ? m.default : mod;
  if (typeof candidate !== "function") {
    return { kind: "error", message: "sharp module did not expose a callable factory" };
  }
  return { kind: "ok", sharp: candidate as SharpFactory };
};