lazyChunks

tcmts.cli.web.lazyChunks · #4bjphpef · term · runs anywhere

Code split for real: what only an import() reaches (a lazy component's code) stays out of the chunk groups (chunkGroup), so it loads when it's needed, in chunks of its own; chunks has them, by the module (its component's hash) that imports them, as paths under /_app/. A server component's tree naming that lazy one has server rendering start loading them with the page (useFlight).

import { CHUNK_GROUPS, HASH_FILE } from "#codebase/tcmts/cli/web";
import type { Plugin } from "vite";

export function lazyChunks(): {
  plugin: Plugin;
  lazyOnly: Set<string>;
  chunks: Map<string, string[]>;
} {
  const lazyOnly = new Set<string>();
  const chunks = new Map<string, string[]>();
  const plugin: Plugin = {
    name: "tcm-lazy",
    buildEnd() {
      // what the entries reach without an import(): the rest is only reached by one
      const ids = [...this.getModuleIds()];
      const reached = new Set<string>();
      const stack = ids.filter((id) => this.getModuleInfo(id)?.isEntry);
      while (stack.length) {
        const id = stack.pop()!;
        if (reached.has(id)) continue;
        reached.add(id);
        stack.push(...(this.getModuleInfo(id)?.importedIds ?? []));
      }
      for (const id of ids) if (!reached.has(id)) lazyOnly.add(id);
    },
    generateBundle(_options, bundle) {
      const groups = new Set<string>(CHUNK_GROUPS);
      for (const out of Object.values(bundle)) {
        if (out.type !== "chunk" || !out.isDynamicEntry || !out.facadeModuleId) continue;
        const files = [
          out.fileName,
          ...out.imports.filter((f) => !groups.has(bundle[f]?.name ?? "")),
        ];
        for (const importer of this.getModuleInfo(out.facadeModuleId)?.dynamicImporters ?? []) {
          const m = HASH_FILE.exec(importer);
          if (m) chunks.set(m[1]!, [...new Set([...(chunks.get(m[1]!) ?? []), ...files])]);
        }
      }
    },
  };
  return { plugin, lazyOnly, chunks };
}

uses

used by