thi-ng/umbrella

View on GitHub
packages/meta-css/src/utils.ts

Summary

Maintainability
A
45 mins
Test Coverage
// thing:no-export
import type { Maybe } from "@thi.ng/api";
import { isString } from "@thi.ng/checks";
import { readText, writeText } from "@thi.ng/file-io";
import type { ILogger } from "@thi.ng/logger";
import { Stream, reactive, sync } from "@thi.ng/rstream";
import { Z4 } from "@thi.ng/strings";
import { assocObj, map } from "@thi.ng/transducers";
import { watch } from "node:fs";
import { resolve } from "node:path";
import type { CompiledSpec, CompiledSpecs } from "./api.js";

export const maybeWriteText = (
    out: Maybe<string>,
    body: string | string[],
    logger: ILogger
) => {
    body = isString(body) ? body : body.join("\n");
    out
        ? writeText(resolve(out), body, logger)
        : process.stdout.write(body + "\n");
};

export const generateHeader = ({ info: { name, version } }: CompiledSpecs) =>
    `/*! ${name} v${version} - generated by thi.ng/meta-css @ ${new Date().toISOString()} */`;

export const watchInputs = (paths: string[], logger: ILogger) => {
    const close = () => {
        logger.info("closing watchers...");
        inputs.forEach((i) => i.watcher.close());
    };
    const inputs = paths.map((file, i) => {
        file = resolve(file);
        const input = reactive(readText(file, logger), {
            id: `in${Z4(i)}`,
        });
        return {
            input,
            watcher: watch(file, {}, (event) => {
                if (event === "change") {
                    try {
                        input.next(readText(file, logger));
                    } catch (e) {
                        logger.warn((<Error>e).message);
                        close();
                    }
                } else {
                    logger.warn(`input removed:`, file);
                    close();
                }
            }),
        };
    });
    // close watchers when ctrl-c is pressed
    process.on("SIGINT", close);
    return sync({
        src: assocObj<Stream<string>>(
            map(
                ({ input }) => <[string, Stream<string>]>[input.id, input],
                inputs
            )
        ),
    });
};

/** @internal */
export const withoutInternals = (props: CompiledSpec) =>
    Object.keys(props).reduce((acc, k) => {
        if (!k.startsWith("__")) acc[k] = props[k];
        return acc;
    }, <CompiledSpec>{});