@baseplate-dev/sync
Version:
Library for syncing Baseplate descriptions
139 lines • 4.81 kB
JavaScript
import { promises as fs } from 'node:fs';
import path from 'node:path';
/**
* Builder for the output of a generator task that collects the files and
* commands that need to be run
*/
export class GeneratorTaskOutputBuilder {
/**
* The output of the generator
*/
output;
/**
* The info of the current generator
*/
generatorInfo;
/**
* The id of the current generator
*/
generatorId;
/**
* The dynamic tasks that have been added to the output
*/
dynamicTasks = [];
/**
* Options for template metadata
*/
metadataOptions;
constructor(context) {
this.output = {
files: new Map(),
postWriteCommands: [],
globalFormatters: [],
};
this.generatorInfo = context.generatorInfo;
this.generatorId = context.generatorId;
this.metadataOptions = context.templateMetadataOptions ?? {
includeTemplateMetadata: false,
shouldGenerateMetadata: () => false,
};
}
/**
* Reads a template file from the generator base directory
*
* @param templatePath The path to the template file relative to the templates directory
* @returns The contents of the template file
*/
readTemplate(templatePath) {
const fullPath = path.join(this.generatorInfo.baseDirectory, 'templates', templatePath);
return fs.readFile(fullPath, 'utf8');
}
/**
* Writes a file to the output
*
* @param payload The payload for the file to write
*/
writeFile({ id, destination: filePath, contents, options, generatorName, templateInfo, }) {
// normalize all paths to POSIX style / paths and remove any preceding @/
const fullPath = filePath
.replaceAll(path.sep, path.posix.sep)
.replace(/^(@\/)?/, '');
if (this.output.files.has(fullPath)) {
throw new Error(`Cannot overwrite file ${fullPath}`);
}
if (contents instanceof Buffer && !options?.skipFormatting) {
throw new Error(`Cannot format Buffer contents for ${fullPath}`);
}
this.output.files.set(fullPath, {
id: `${generatorName ?? this.generatorInfo.name}:${id}`,
contents,
options: this.metadataOptions.includeTemplateMetadata && templateInfo
? {
...options,
templateInfo,
}
: options,
});
}
/**
* Adds a post write command to the output
*
* @param command The command to run
* @param commandType The type of the command
* @param options The options for the command
*/
addPostWriteCommand(command, options) {
this.output.postWriteCommands.push({ command, options });
}
/**
* Applies one or more actions to the builder
*
* @param actions The actions to apply
*/
async apply(...actions) {
for (const action of actions) {
await action.execute(this);
}
}
/**
* Adds a formatter to the output that will be applied to all files depending on their extension
*
* @param formatter The formatter to add
*/
addGlobalFormatter(formatter) {
// check if formatter already exists for given extensions
const existingFormatter = this.output.globalFormatters.find((f) => f.fileExtensions?.some((ext) => formatter.fileExtensions?.includes(ext)));
if (existingFormatter) {
throw new Error(`Formatter ${formatter.name} already exists for file extensions ${formatter.fileExtensions?.join(', ')}`);
}
if (formatter.fileNames) {
const existingFormatter = this.output.globalFormatters.find((f) => f.fileNames?.some((name) => formatter.fileNames?.includes(name)));
if (existingFormatter) {
throw new Error(`Formatter ${formatter.name} already exists for file names ${formatter.fileNames.join(', ')}`);
}
}
this.output.globalFormatters.push(formatter);
}
/**
* Adds a dynamic task to the output
*
* @param name The name of the task
* @param task The task to add
*/
addDynamicTask(name, task) {
if (this.dynamicTasks.some((t) => t.name === name)) {
throw new Error(`Dynamic task ${name} already exists`);
}
if (!task.phase) {
throw new Error(`Dynamic task ${name} must have a phase`);
}
this.dynamicTasks.push({
id: `${this.generatorId}#${name}`,
name,
task,
generatorId: this.generatorId,
generatorInfo: this.generatorInfo,
});
}
}
//# sourceMappingURL=generator-task-output.js.map