core/coordinators/template.coordinator.ts
core/coordinators/template.coordinator.ts is a file in GovLab Docs. 110 lines of code and 25 definitions.
import {
MISSING_SUBJECT,
alreadyExists,
brokenName,
concernHint,
created,
memberHint,
notActivity,
placeholderSummary,
unknownForm,
unplaceable,
} from "#configuration/strings/template.strings";
import type { TemplateArguments, TemplateContext } from "#types/environment.types";
import { dirname, join } from "node:path";
import { existsSync, mkdirSync } from "node:fs";
import type { DocForm } from "#types/location.types";
import { computeLocation } from "#core/resolvers/location.resolver";
import { docName } from "#core/validators/filename.validator";
import { print } from "#core/reporters/base.reporter";
import { renderTemplate } from "#core/formatters/template.formatter";
import { writeVerbatim } from "@govlab/canonical-write";
const LIST_SEPARATOR = ", ";
const VERB_JOIN = "-";
const DIRECTIVE_MOOD = "directive";
const MODULE_AXIS = "module";
const CURRENT_STATUS = "current";
const PLANNED_STATUS = "planned";
const CONCERN_REASONS: ReadonlySet<string> = new Set(["unknown-concern", "missing-concern"]);
const MEMBER_REASONS: ReadonlySet<string> = new Set(["unmarked-member", "unknown-member"]);
const resolveForm = function resolveForm(context: TemplateContext, form: string): DocForm {
const def = context.userReg.forms[form];
if (def === undefined || def.boundary) {
const valid = Object.entries(context.userReg.forms).flatMap(([id, entry]) => (entry.boundary ? [] : [id]));
throw new Error(unknownForm(form, valid.join(LIST_SEPARATOR)));
}
return def;
};
const composedName = function composedName(context: TemplateContext, def: DocForm, args: TemplateArguments): string {
if (args.name.length > 0) {
return args.name;
}
if (args.subject.length === 0) {
throw new Error(MISSING_SUBJECT);
}
if (def.mood !== DIRECTIVE_MOOD) {
return args.subject;
}
const verb = context.userReg.activityVerbs[args.concern];
if (verb === undefined) {
const activities = Object.keys(context.userReg.activityVerbs).join(LIST_SEPARATOR);
throw new Error(notActivity(def.id, args.concern, activities));
}
return [verb, args.subject].join(VERB_JOIN);
};
const assertName = function assertName(context: TemplateContext, def: DocForm, concern: string, name: string): void {
const [defect] = docName({ activityVerbs: context.userReg.activityVerbs, concern, form: def, name });
if (defect !== undefined) {
throw new Error(brokenName(name, defect.detail));
}
};
const hintFor = function hintFor(context: TemplateContext, reason: string): string {
if (CONCERN_REASONS.has(reason)) {
return concernHint(context.userReg.concerns.join(LIST_SEPARATOR));
}
return MEMBER_REASONS.has(reason) ? memberHint((context.registries.owners ?? []).join(LIST_SEPARATOR)) : "";
};
const locate = function locate(context: TemplateContext, args: TemplateArguments, name: string): string {
const result = computeLocation({
concern: args.concern,
form: args.form,
member: args.member,
name,
options: context.locationOptions,
registries: context.registries,
});
if (!result.ok) {
throw new Error(unplaceable(result.reason, result.detail, hintFor(context, result.reason)));
}
return result.path;
};
const statusFor = function statusFor(def: DocForm, requested: string): string {
if (requested.length > 0) {
return requested;
}
return def.ownerAxis === MODULE_AXIS ? CURRENT_STATUS : PLANNED_STATUS;
};
export const runNew = function runNew(context: TemplateContext, args: TemplateArguments): void {
const def = resolveForm(context, args.form);
const name = composedName(context, def, args);
assertName(context, def, args.concern, name);
const path = locate(context, args, name);
const absolute = join(context.root, path);
if (existsSync(absolute)) {
throw new Error(alreadyExists(path));
}
const summary = args.summary.length > 0 ? args.summary : placeholderSummary(name);
mkdirSync(dirname(absolute), { recursive: true });
writeVerbatim(
absolute,
renderTemplate({
concern: args.concern,
def,
form: args.form,
name,
status: statusFor(def, args.status),
summary,
}),
);
print(created(path));
};