chore: import upstream snapshot with attribution
i18n - Build Validation / Validate i18n Builds (24) (push) Has been cancelled
CI - Node.js / Lint (24) (push) Has been cancelled
CI - Node.js / Build (24) (push) Has been cancelled
CI - Node.js / Test (24) (push) Has been cancelled
CI - Node.js / Test - Upcoming Changes (24) (push) Has been cancelled
CI - Node.js / Test - i18n (italian, 24) (push) Has been cancelled
CI - Node.js / Test - i18n (portuguese, 24) (push) Has been cancelled
CD - Docker - GHCR Images / Build and Push Images (push) Has been cancelled
i18n - Build Validation / Validate i18n Builds (24) (push) Has been cancelled
CI - Node.js / Lint (24) (push) Has been cancelled
CI - Node.js / Build (24) (push) Has been cancelled
CI - Node.js / Test (24) (push) Has been cancelled
CI - Node.js / Test - Upcoming Changes (24) (push) Has been cancelled
CI - Node.js / Test - i18n (italian, 24) (push) Has been cancelled
CI - Node.js / Test - i18n (portuguese, 24) (push) Has been cancelled
CD - Docker - GHCR Images / Build and Push Images (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
const { root } = require('mdast-builder');
|
||||
const find = require('unist-util-find');
|
||||
const { getSection, getAllSections } = require('./utils/get-section');
|
||||
const getAllBefore = require('./utils/before-heading');
|
||||
const { getParagraphContent } = require('./utils/get-paragraph-content');
|
||||
|
||||
const { splitOnThematicBreak } = require('./utils/split-on-thematic-break');
|
||||
const { createMdastToHtml } = require('./utils/i18n-stringify');
|
||||
|
||||
function plugin() {
|
||||
return transformer;
|
||||
function transformer(tree, file) {
|
||||
const toHtml = createMdastToHtml(file.data.lang);
|
||||
|
||||
function getQuestion(textNodes, answersNodes, solutionNodes) {
|
||||
const text = toHtml(textNodes);
|
||||
const answers = getAnswers(answersNodes);
|
||||
const solution = getSolution(solutionNodes);
|
||||
|
||||
if (!text) throw Error('text is missing from question');
|
||||
if (!answers) throw Error('answers are missing from question');
|
||||
if (!solution) throw Error('solution is missing from question');
|
||||
if (solution > answers.length)
|
||||
throw Error(
|
||||
`solution must be within range of number of answers: 1-${answers.length}`
|
||||
);
|
||||
if (answers[solution - 1].feedback)
|
||||
throw Error('answer selected as solution cannot have feedback section');
|
||||
|
||||
return { text, answers, solution };
|
||||
}
|
||||
|
||||
function getAnswers(answersNodes) {
|
||||
const answerGroups = splitOnThematicBreak(answersNodes);
|
||||
|
||||
return answerGroups.map((answerGroup, index) => {
|
||||
const answerTree = root(answerGroup);
|
||||
const feedbackGroups = getAllSections(answerTree, '--feedback--');
|
||||
|
||||
if (feedbackGroups.length > 1)
|
||||
throw new Error(`answer ${index + 1} has multiple feedback sections`);
|
||||
|
||||
const [feedbackNodes] = feedbackGroups;
|
||||
const audioIdNodes = getSection(answerTree, '--audio-id--');
|
||||
const hasFeedback = feedbackNodes?.length > 0;
|
||||
const hasAudioId = audioIdNodes.length > 0;
|
||||
|
||||
if (hasFeedback || hasAudioId) {
|
||||
let answerNodes;
|
||||
|
||||
if (hasFeedback && hasAudioId) {
|
||||
const feedbackHeading = find(answerTree, {
|
||||
type: 'heading',
|
||||
children: [{ type: 'text', value: '--feedback--' }]
|
||||
});
|
||||
const audioIdHeading = find(answerTree, {
|
||||
type: 'heading',
|
||||
children: [{ type: 'text', value: '--audio-id--' }]
|
||||
});
|
||||
|
||||
const feedbackIndex = answerTree.children.indexOf(feedbackHeading);
|
||||
const audioIdIndex = answerTree.children.indexOf(audioIdHeading);
|
||||
const firstMarker =
|
||||
feedbackIndex < audioIdIndex ? '--feedback--' : '--audio-id--';
|
||||
answerNodes = getAllBefore(answerTree, firstMarker);
|
||||
} else if (hasFeedback) {
|
||||
answerNodes = getAllBefore(answerTree, '--feedback--');
|
||||
} else {
|
||||
answerNodes = getAllBefore(answerTree, '--audio-id--');
|
||||
}
|
||||
|
||||
if (answerNodes.length < 1) {
|
||||
throw Error('Answer missing');
|
||||
}
|
||||
|
||||
let extractedAudioId = null;
|
||||
if (hasAudioId) {
|
||||
const audioIdContent = getParagraphContent(audioIdNodes[0]);
|
||||
if (audioIdContent && audioIdContent.trim()) {
|
||||
extractedAudioId = audioIdContent.trim();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
answer: toHtml(answerNodes),
|
||||
feedback: hasFeedback ? toHtml(feedbackNodes) : null,
|
||||
audioId: extractedAudioId
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
answer: toHtml(answerGroup),
|
||||
feedback: null,
|
||||
audioId: null
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function getSolution(solutionNodes) {
|
||||
let solution;
|
||||
try {
|
||||
solution = Number(getParagraphContent(solutionNodes[0]));
|
||||
if (Number.isNaN(solution)) throw Error('Not a number');
|
||||
if (solution < 1) throw Error('Not positive number');
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
throw Error('A video solution should be a positive integer');
|
||||
}
|
||||
|
||||
return solution;
|
||||
}
|
||||
|
||||
const allQuestionNodes = getSection(tree, '--questions--');
|
||||
|
||||
if (allQuestionNodes.length > 0) {
|
||||
const questions = [];
|
||||
const questionTrees = [];
|
||||
|
||||
allQuestionNodes.forEach(questionNode => {
|
||||
const isStartOfQuestion =
|
||||
questionNode.children?.[0]?.value === '--text--';
|
||||
if (isStartOfQuestion) {
|
||||
questionTrees.push([questionNode]);
|
||||
} else if (questionTrees.length === 0) {
|
||||
throw Error('question text is missing in questions section');
|
||||
} else {
|
||||
questionTrees[questionTrees.length - 1].push(questionNode);
|
||||
}
|
||||
});
|
||||
|
||||
questionTrees.forEach(questionNodes => {
|
||||
const questionTree = root(questionNodes);
|
||||
|
||||
const textNodes = getSection(questionTree, '--text--');
|
||||
const answersNodes = getSection(questionTree, '--answers--');
|
||||
const solutionNodes = getSection(questionTree, '--video-solution--');
|
||||
|
||||
questions.push(getQuestion(textNodes, answersNodes, solutionNodes));
|
||||
});
|
||||
|
||||
file.data.questions = questions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = plugin;
|
||||
Reference in New Issue
Block a user