feat(challenge-md): Parse seed files from md

This commit is contained in:
Bouncey
2018-09-30 20:13:52 +01:00
committed by Stuart Taylor
parent 8398522021
commit 2f193ac4ae
8 changed files with 228 additions and 6 deletions

View File

@ -0,0 +1,23 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`challengeSeed-to-data plugin should have an output to match the snapshot 1`] = `
Object {
"files": Array [
Object {
"contents": "function testFunction(arg) {
return arg;
}
testFunction('hello');
",
"ext": "js",
"head": "console.log('before the test');
",
"key": "indexjs",
"name": "index",
"tail": "console.info('after the test');
",
},
],
}
`;

View File

@ -0,0 +1,72 @@
const visit = require('unist-util-visit');
const { selectAll, select } = require('hast-util-select');
const { sectionFilter } = require('./utils');
const seedRE = /(.+)-seed$/;
const headRE = /(.+)-setup$/;
const tailRE = /(.+)-teardown$/;
function defaultFile(lang) {
return {
key: `index${lang}`,
ext: lang,
name: 'index',
contents: '',
head: '',
tail: ''
};
}
function createCodeGetter(key, regEx, seeds) {
return container => {
const {
properties: { id }
} = container;
const lang = id.match(regEx)[1];
const code = select('code', container).children[0].value;
if (lang in seeds) {
seeds[lang] = {
...seeds[lang],
[key]: code
};
} else {
seeds[lang] = {
...defaultFile(lang),
[key]: code
};
}
};
}
function createPlugin() {
return function transformer(tree, file) {
function visitor(node) {
if (sectionFilter(node, 'challengeSeed')) {
let seeds = {};
const codeDivs = selectAll('div', node);
const seedConatiners = codeDivs.filter(({ properties: { id } }) =>
seedRE.test(id)
);
seedConatiners.forEach(createCodeGetter('contents', seedRE, seeds));
const headConatiners = codeDivs.filter(({ properties: { id } }) =>
headRE.test(id)
);
headConatiners.forEach(createCodeGetter('head', headRE, seeds));
const tailConatiners = codeDivs.filter(({ properties: { id } }) =>
tailRE.test(id)
);
tailConatiners.forEach(createCodeGetter('tail', tailRE, seeds));
file.data = {
...file.data,
files: Object.keys(seeds).map(lang => seeds[lang])
};
}
}
visit(tree, 'element', visitor);
};
}
module.exports = createPlugin;

View File

@ -0,0 +1,59 @@
/* global describe it expect beforeEach */
const mockAST = require('./fixtures/challenge-html-ast.json');
const challengeSeedToData = require('./challengeSeed-to-data');
describe('challengeSeed-to-data plugin', () => {
const plugin = challengeSeedToData();
let file = { data: {} };
beforeEach(() => {
file = { data: {} };
});
it('returns a function', () => {
expect(typeof plugin).toEqual('function');
});
it('adds a `files` property to `file.data`', () => {
plugin(mockAST, file);
expect('files' in file.data).toBe(true);
});
it('ensures that the `files` property is an array', () => {
plugin(mockAST, file);
expect(Array.isArray(file.data.files)).toBe(true);
});
it('adds test objects to the files array following a schema', () => {
expect.assertions(7);
plugin(mockAST, file);
const {
data: { files }
} = file;
const testObject = files[0];
expect(Object.keys(testObject).length).toEqual(6);
expect(testObject).toHaveProperty('key');
expect(testObject).toHaveProperty('ext');
expect(testObject).toHaveProperty('name');
expect(testObject).toHaveProperty('contents');
expect(testObject).toHaveProperty('head');
expect(testObject).toHaveProperty('tail');
});
it('only adds strings to the `files` object type', () => {
expect.assertions(6);
plugin(mockAST, file);
const {
data: { files }
} = file;
const testObject = files[0];
Object.keys(testObject)
.map(key => testObject[key])
.forEach(value => expect(typeof value).toEqual('string'));
});
it('should have an output to match the snapshot', () => {
plugin(mockAST, file);
expect(file.data).toMatchSnapshot();
});
});

View File

@ -10,6 +10,7 @@ const raw = require('rehype-raw');
const frontmatterToData = require('./frontmatter-to-data');
const textToData = require('./text-to-data');
const testsToData = require('./tests-to-data');
const challengeSeedToData = require('./challengeSeed-to-data');
const processor = unified()
.use(markdown)
@ -19,6 +20,7 @@ const processor = unified()
.use(remark2rehype, { allowDangerousHTML: true })
.use(raw)
.use(textToData, ['description', 'instructions'])
.use(challengeSeedToData)
// the plugins below are just to stop the processor from throwing
// we need to write a compiler that can create graphql nodes
.use(html);

View File

@ -821,6 +821,11 @@
}
}
},
"bcp-47-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-1.0.0.tgz",
"integrity": "sha512-6rzWe2U70JHrYfBqBnhoeV7HykhmCQ6X2RrBcbpkyCwtNpkGadr3m3LF8ZLXpQ7qrsLCOm3eis2lsVemkLsvHg=="
},
"bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
@ -831,6 +836,11 @@
"tweetnacl": "0.14.5"
}
},
"boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@ -1156,6 +1166,11 @@
}
}
},
"css-selector-parser": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-1.3.0.tgz",
"integrity": "sha1-XxrUPi2O77/cME/NOaUhZklD4+s="
},
"cssom": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.4.tgz",
@ -1330,6 +1345,11 @@
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
"dev": true
},
"direction": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/direction/-/direction-1.0.2.tgz",
"integrity": "sha512-hSKoz5FBn+zhP9vWKkVQaaxnRDg3/MoPdcg2au54HIUDR8MrP8Ah1jXSJwCXel6SV3Afh5DSzc8Uqv2r1UoQwQ=="
},
"domexception": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz",
@ -2424,6 +2444,11 @@
"xtend": "4.0.1"
}
},
"hast-util-has-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-1.0.1.tgz",
"integrity": "sha512-DUck5lp8ku3o8n9GIA1Nghdz8UQyis2/b/ro0O4z5HP/y82uzZL6CXehuQmY5re+rLgTP4MVF/YpYDj9YqD0wA=="
},
"hast-util-is-element": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-1.0.1.tgz",
@ -2456,6 +2481,27 @@
}
}
},
"hast-util-select": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-2.1.0.tgz",
"integrity": "sha512-wPnaITEMGvTvzA9GUQhj7qyOVLceQfxol3Zj+2BrhuMQNt7/W1Za76ZDspNeRKAZmT4aFikNa2SwuJ7MaQC6LQ==",
"requires": {
"bcp-47-match": "1.0.0",
"comma-separated-tokens": "1.0.5",
"css-selector-parser": "1.3.0",
"direction": "1.0.2",
"hast-util-has-property": "1.0.1",
"hast-util-is-element": "1.0.1",
"hast-util-to-string": "1.0.1",
"hast-util-whitespace": "1.0.1",
"not": "0.1.0",
"nth-check": "1.0.1",
"property-information": "4.2.0",
"space-separated-tokens": "1.1.2",
"unist-util-visit": "1.4.0",
"zwitch": "1.0.3"
}
},
"hast-util-to-html": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-4.0.1.tgz",
@ -2485,6 +2531,11 @@
"zwitch": "1.0.3"
}
},
"hast-util-to-string": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-1.0.1.tgz",
"integrity": "sha512-EC6awGe0ZMUNYmS2hMVaKZxvjVtQA4RhXjtgE20AxGG49MM7OUUfaHc6VcVYv2YwzNlrZQGe5teimCxW1Rk+fA=="
},
"hast-util-whitespace": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-1.0.1.tgz",
@ -3923,6 +3974,11 @@
"remove-trailing-separator": "1.1.0"
}
},
"not": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/not/-/not-0.1.0.tgz",
"integrity": "sha1-yWkcF0bFXc++VMvYvU/wQbwrUZ0="
},
"npm-run-path": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
@ -3932,6 +3988,14 @@
"path-key": "2.0.1"
}
},
"nth-check": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz",
"integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=",
"requires": {
"boolbase": "1.0.0"
}
},
"number-is-nan": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",

View File

@ -13,6 +13,7 @@
"jest": "^23.6.0"
},
"dependencies": {
"hast-util-select": "^2.1.0",
"hast-util-to-html": "^4.0.1",
"js-yaml": "^3.12.0",
"lodash": "^4.17.11",

View File

@ -1,12 +1,7 @@
const visit = require('unist-util-visit');
const toHTML = require('hast-util-to-html');
const sectionFilter = (
{ type, tagName, properties: { id = '' } },
sectionId
) => {
return type === 'element' && tagName === 'section' && id === sectionId;
};
const { sectionFilter } = require('./utils')
function textToData(sectionIds) {
if (!sectionIds || !Array.isArray(sectionIds) || sectionIds.length <= 0) {

View File

@ -0,0 +1,6 @@
exports.sectionFilter = (
{ type, tagName, properties: { id = '' } },
sectionId
) => {
return type === 'element' && tagName === 'section' && id === sectionId;
};