57 lines
1.3 KiB
TypeScript
57 lines
1.3 KiB
TypeScript
import { AsyncLocalStorage } from "node:async_hooks";
|
|
import type { ComposeSpecification } from "../schema/docker.d";
|
|
import { getProject, throwWhen } from "./shared";
|
|
import { readCompose, resolveComposeFile } from "./yaml";
|
|
|
|
type AppContext = {
|
|
project: string;
|
|
cwd: string;
|
|
path(): Promise<string | undefined>;
|
|
compose(): Promise<ComposeSpecification>;
|
|
};
|
|
|
|
const appContext = new AsyncLocalStorage<AppContext>();
|
|
|
|
export function provideContext<T>(fn: () => T): T {
|
|
const cwd = process.cwd();
|
|
const path = createCachedGetter("composeFile", () => resolveComposeFile(cwd));
|
|
return appContext.run(
|
|
{
|
|
project: getProject(),
|
|
cwd,
|
|
path,
|
|
compose: createCachedGetter("compose", () =>
|
|
path()
|
|
.then(
|
|
throwWhen(
|
|
(path): path is undefined => path === undefined,
|
|
"Compose file cannot be found",
|
|
),
|
|
)
|
|
.then(readCompose),
|
|
),
|
|
},
|
|
fn,
|
|
);
|
|
}
|
|
|
|
export function ctx() {
|
|
const store = appContext.getStore();
|
|
if (!store) throw new Error("Not in context!");
|
|
return store;
|
|
}
|
|
|
|
export function createCachedGetter<T>(
|
|
_key: string,
|
|
fn: () => Promise<T>,
|
|
): () => Promise<T> {
|
|
let cached: Promise<T> | undefined;
|
|
return () => {
|
|
cached ??= fn().catch((error) => {
|
|
cached = undefined;
|
|
throw error;
|
|
});
|
|
return cached;
|
|
};
|
|
}
|