core/plugins/site.plugin.ts

core/plugins/site.plugin.ts is a file in Bane's Lab Build Scripts. 96 lines of code and 15 definitions.

import type { Discovery, SitePhase, SiteState, SiteStep } from "#types/site.types";
import type { Plugin, ResolvedConfig } from "vite";
import { catalogMiddleware, payloadMiddleware } from "#core/coordinators/payload.coordinator";
import { discoverSteps, runPhase } from "#core/pipelines/site.pipeline";
import type { DevState } from "#types/page.types";
import type { DiagramWalk } from "#types/diagram.types";
import { buildCatalog } from "#core/coordinators/catalog.coordinator";
import { composeOntology } from "#core/coordinators/ontology.coordinator";
import { createRelinker } from "#core/coordinators/link.coordinator";
import { discover } from "#core/converters/site.converter";
import { loadFrom } from "#core/loaders/site.loader";
import process from "node:process";

const BUILD_COMMAND = "build";

const initialState = function initialState(config: ResolvedConfig, diagrams: DiagramWalk): SiteState {
    return {
        diagrams,
        mode: config.command === BUILD_COMMAND ? "build" : "serve",
        ontology: null,
        outDir: config.build.outDir,
        root: config.root,
    };
};

const write = function write(line: string): void {
    process.stdout.write(line);
};

interface Held {
    config: ResolvedConfig | null;
    state: SiteState | null;
    steps: readonly SiteStep[];
}

export const sitePlugin = function sitePlugin(diagrams: DiagramWalk): Plugin {
    const held: Held = { config: null, state: null, steps: [] };
    const phase = async function phase(name: SitePhase, state: SiteState): Promise<void> {
        await runPhase(held.steps, name, state, write).then((next) => {
            held.state = next;
        });
    };
    return {
        buildStart: {
            async handler() {
                if (held.config !== null) {
                    await phase("start", initialState(held.config, diagrams));
                }
            },
            order: "pre",
            sequential: true,
        },
        closeBundle: {
            async handler() {
                if (held.state !== null) {
                    await phase("close", held.state);
                }
            },
            sequential: true,
        },
        async configResolved(config) {
            held.config = config;
            held.steps = await discoverSteps();
        },
        configureServer(server) {
            let state: Promise<DevState> | null = null;
            let catalog: Promise<ReadonlyMap<string, string>> | null = null;
            const relink = createRelinker(server.config.root, (error) => {
                server.config.logger.error(String(error));
            });
            server.watcher.on("change", (file) => {
                state = null;
                catalog = null;
                relink(file);
            });
            const current = async (): Promise<DevState> => {
                state ??= loadFrom(server).then(async (loaded) => ({ discovery: await discover(loaded), loaded }));
                return state;
            };
            const discovery = async (): Promise<Discovery> => (await current()).discovery;
            server.middlewares.use(
                catalogMiddleware(discovery, async () => {
                    catalog ??= current().then(
                        async (loaded) =>
                            (
                                await buildCatalog(
                                    loaded.loaded.runner,
                                    loaded.discovery,
                                    loaded.loaded.exportSections,
                                    composeOntology(),
                                )
                            ).files,
                    );
                    return catalog;
                }),
            );
            server.middlewares.use(payloadMiddleware(discovery));
        },
        name: "site",
    };
};