@nodecg/types
Version:
Dynamic broadcast graphics rendered in a browser
118 lines (107 loc) • 3.08 kB
text/typescript
import path from "node:path";
import * as E from "fp-ts/Either";
import { flow, pipe } from "fp-ts/function";
import * as IOE from "fp-ts/IOEither";
import * as O from "fp-ts/Option";
import type { NodeCG } from "../../types/nodecg";
import { readJsonFileSync } from "../util-fp/read-json-file-sync";
import { parseAssets } from "./assets";
import { parseBundleConfig, parseDefaults } from "./config";
import { parseExtension } from "./extension";
import { parseGit } from "./git";
import { parseGraphics } from "./graphics";
import { parseManifest } from "./manifest";
import { parseMounts } from "./mounts";
import { parsePanels } from "./panels";
import { parseSounds } from "./sounds";
const readBundlePackageJson = (bundlePath: string) =>
pipe(
bundlePath,
(bundlePath: string) => path.join(bundlePath, "package.json"),
readJsonFileSync,
IOE.map((json) => json as NodeCG.PackageJSON),
IOE.mapLeft((error) => {
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
return new Error(
`Bundle at path ${bundlePath} does not contain a package.json!`,
);
}
if (error instanceof SyntaxError) {
return new Error(
`${bundlePath}'s package.json is not valid JSON, please check it against a validator such as jsonlint.com`,
);
}
return error;
}),
);
const parseBundleNodecgConfig = flow(
(bundlePath: string) => path.join(bundlePath, "nodecg.config.js"),
IOE.tryCatchK(require, E.toError),
IOE.match(
() => ({}),
(config) => config.default || config,
),
IOE.fromIO,
IOE.flatMap((config) => {
if (
typeof config !== "object" ||
config === null ||
Array.isArray(config)
) {
return IOE.left(new Error("nodecg.config.js must export an object"));
}
return IOE.right(config as NodeCG.NodecgBundleConfig);
}),
);
export const parseBundle = (
bundlePath: string,
bundleCfg?: NodeCG.Bundle.UnknownConfig,
): NodeCG.Bundle => {
const manifest = pipe(
bundlePath,
readBundlePackageJson,
IOE.flatMap(parseManifest(bundlePath)),
IOE.getOrElse((error) => {
throw error;
}),
)();
const dashboardDir = path.resolve(bundlePath, "dashboard");
const graphicsDir = path.resolve(bundlePath, "graphics");
const nodecgBundleConfig = pipe(
parseBundleNodecgConfig(bundlePath),
IOE.getOrElse((error) => {
throw error;
}),
)();
const config = pipe(
bundleCfg,
O.fromNullable,
O.match(
() => parseDefaults(manifest.name)(bundlePath),
IOE.tryCatchK(
(bundleCfg) => parseBundleConfig(manifest.name, bundlePath, bundleCfg),
E.toError,
),
),
IOE.getOrElse((error) => {
throw error;
}),
)();
const bundle: NodeCG.Bundle = {
...manifest,
dir: bundlePath,
config,
dashboard: {
dir: dashboardDir,
panels: parsePanels(dashboardDir, manifest),
},
mount: parseMounts(manifest),
graphics: parseGraphics(graphicsDir, manifest),
assetCategories: parseAssets(manifest),
hasExtension: parseExtension(bundlePath, manifest),
git: parseGit(bundlePath),
...parseSounds(bundlePath, manifest),
nodecgBundleConfig,
};
return bundle;
};