freeCodeCamp/tools/challenge-md-parser/mdx/plugins/replace-imports.js

92 lines
2.6 KiB
JavaScript

const path = require('path');
const { read } = require('to-vfile');
const modifyChildren = require('unist-util-modify-children');
const remark = require('remark');
const remove = require('unist-util-remove');
const visit = require('unist-util-visit');
const { selectAll } = require('unist-util-select');
const { isEmpty } = require('lodash');
const { editableRegionMarker } = require('./add-seed');
const tableAndStrikeThrough = require('./table-and-strikethrough');
async function parse(file) {
return await remark()
.use(tableAndStrikeThrough)
.parse(file);
}
function plugin() {
return transformer;
function transformer(tree, file, next) {
const importedFiles = selectAll('leafDirective[name=import]', tree);
if (!file) {
next('replace-imports must be passed a file');
return;
}
if (isEmpty(importedFiles)) {
next();
return;
}
const importPromises = importedFiles.map(async ({ attributes }) => {
const { from, component } = attributes;
const location = path.resolve(file.dirname, from);
return await read(location)
.then(parse)
.then(importedFile => {
function modifier(node, index, parent) {
const { type, name, attributes } = node;
const target = attributes ? attributes.component : null;
if (
type === 'leafDirective' &&
name === 'use' &&
target === component
) {
if (!validateImports(importedFile))
throw Error(
'Importing files containing ' +
editableRegionMarker +
's is not supported.'
);
parent.children.splice(index, 1, ...importedFile.children);
}
}
const modify = modifyChildren(modifier);
modify(tree);
});
});
// We're not interested in the results of importing, we just want to
// modify the tree and pass that new tree to follow plugins - as a result,
// we can't just use .then(next), as it would pass the array into next.
// Also, we remove the import statements here.
Promise.all(importPromises)
.then(() => {
remove(tree, { type: 'leafDirective', name: 'import' });
next();
})
.catch(next);
}
}
function validateImports(fileTree) {
let valid = true;
function visitor({ value }) {
if (value && value.includes(editableRegionMarker)) {
valid = false;
return visit.EXIT;
} else {
return visit.CONTINUE;
}
}
visit(fileTree, visitor);
return valid;
}
module.exports = plugin;