Skip to content

support custom SSR render contexts in API routes #1694

Description

@thescientist13

Current State

As observed in thescientist13/greenwood-lit-ssr#41 and per Greenwood's design in which API Routes are "BYOB" (and so should probably be better documented / called out), we need to extend setting up an SSR environment for API routes too. Otherwise, as in the case with Lit SSR, we would get an error that CSSStyleSheet is not defined.

The reason this works for WCC without a custom API route handler, is that WCC automatically loads its own DOM shim as soon as you import it

import { renderFromHTML } from "wc-compiler";

export default function handler(request) {
  // ...
}

But with Lit, as we discovered, order matters, so this should be an optional feature that a renderer plugin should be able to support.

Desired State

A Renderer plugin should effectively be able to provide a custom SSR environment for handling API routes, so from the linked example

// plugin config
const greenwoodPluginRendererLit = () => {
  return [
    // ...
    {
      type: "resource",
      name: "plugin-renderer-lit:resource",
      provider: (compilation, options) => new LitHydrationResource(compilation, options),
    },
  ];
};

export { greenwoodPluginRendererLit };
// API Route Worker Example for Lit SSR
// https://github.com/nodejs/modules/issues/307#issuecomment-858729422
import { parentPort } from "node:worker_threads";
import { transformKoaRequestIntoStandardRequest } from "@greenwood/cli/src/lib/resource-utils.js";
import "@lit-labs/ssr-dom-shim/register-css-hook.js";

async function responseAsObject(response) {
 // ...
}

async function executeRouteModule({ href, request, params }) {
 const { body, headers = {}, method, url } = request;
 const contentType = headers["content-type"] || "";
 const { handler } = await import(new URL(href));
 const format = contentType.startsWith("application/json") ? JSON.parse(body) : body;

 // handling of serialized FormData across Worker threads
 if (contentType.startsWith("x-greenwood/www-form-urlencoded")) {
   headers["content-type"] = "application/x-www-form-urlencoded";
 }

 const response = await handler(
   transformKoaRequestIntoStandardRequest(new URL(url), {
     method,
     header: headers,
     body: format,
   }),
   {
     params,
   },
 );

 parentPort.postMessage(await responseAsObject(response));
}

parentPort.on("message", async (task) => {
 await executeRouteModule(task);
});

Additional Context

Additional tasks to do

  1. Make some of these utils shared
  2. Confirm if isolation mode makes a difference
  3. Make website docs clearer that API Routes are "BYOB"
  4. Update Lit README to call out that register hook is needed in the API route if using CSS Module Scripts (and general API Route usage docs in general)
  5. Update Renderer plugin website docs for apiRouteWorker option
  6. Update types
  7. Tests

Metadata

Metadata

Labels

PluginsGreenwood PluginsSSRalpha.7documentationGreenwood specific docsenhancementImprove something existing (e.g. no docs, new APIs, etc)v0.34.0
No fields configured for Feature.

Projects

Status
👀 In review

Relationships

None yet

Development

No branches or pull requests

Issue actions