harmonyc
Version:
Harmony Code - model-driven BDD for Vitest
108 lines (107 loc) • 3.74 kB
JavaScript
import { readFile, writeFile } from 'fs/promises';
import c from 'tinyrainbow';
import { Project } from 'ts-morph';
import { compileFeature } from "../compiler/compile.js";
import { preprocess } from "../compiler/compiler.js";
import { PhrasesAssistant } from "../phrases_assistant/phrases_assistant.js";
const DEFAULT_OPTIONS = {
autoEditPhrases: true,
};
export default function harmonyPlugin(opts = {}) {
const options = { ...DEFAULT_OPTIONS, ...opts };
const project = new Project();
return {
name: 'harmony',
resolveId(id) {
if (id.endsWith('.harmony')) {
return id;
}
},
transform(code, id) {
if (!id.endsWith('.harmony'))
return null;
code = preprocess(code);
const { outFile, phraseMethods, featureClassName } = compileFeature(id, code, opts);
if (options.autoEditPhrases) {
void updatePhrasesFile(project, id, phraseMethods, featureClassName);
}
return {
code: outFile.valueWithoutSourceMap,
map: outFile.sourceMap,
};
},
config(config) {
var _a, _b;
var _c;
(_a = config.test) !== null && _a !== void 0 ? _a : (config.test = {});
(_b = (_c = config.test).reporters) !== null && _b !== void 0 ? _b : (_c.reporters = ['default']);
if (!Array.isArray(config.test.reporters)) {
config.test.reporters = [config.test.reporters];
}
config.test.reporters.splice(0, 0, new HarmonyReporter());
},
};
}
class HarmonyReporter {
onInit(ctx) {
this.ctx = ctx;
}
onCollected(files) {
this.files = files;
}
onTaskUpdate(packs) {
if (this.files)
for (const file of this.files)
addPhrases(file);
}
onFinished(files, errors) {
for (const file of files)
addPhrases(file);
}
}
function addPhrases(task, depth = 2) {
var _a, _b;
if ('tasks' in task) {
for (const child of task.tasks)
addPhrases(child, depth + 1);
}
else if (task.type === 'test' &&
((_a = task.result) === null || _a === void 0 ? void 0 : _a.state) === 'fail' &&
((_b = task.meta) === null || _b === void 0 ? void 0 : _b.hasOwnProperty('phrases')) &&
task.meta.phrases.length > 0) {
const x = task;
x.name +=
'\n' +
task.meta
.phrases.map((step, i, a) => {
const indent = ' '.repeat(depth);
const failed = i === a.length - 1;
const figure = failed ? c.red('×') : c.green('✓');
return `${indent}${figure} ${step}`;
})
.join('\n');
delete task.meta.phrases; // to make sure not to add them again
}
}
async function updatePhrasesFile(project, id, phraseMethods, featureClassName) {
try {
const phrasesFile = id.replace(/\.harmony$/, '.phrases.ts');
let phrasesFileContent = '';
try {
phrasesFileContent = await readFile(phrasesFile, 'utf-8');
}
catch {
// File doesn't exist
}
const pa = new PhrasesAssistant(project, phrasesFile, featureClassName);
pa.ensureMethods(phraseMethods);
const newContent = pa.toCode();
if (newContent === phrasesFileContent) {
return; // No changes needed
}
await writeFile(phrasesFile, newContent, 'utf-8');
}
catch (e) {
console.error('Error updating phrases file:', e);
}
}