feat(challenge-md): Parse seed files from md
This commit is contained in:
@ -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');
|
||||
",
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
72
tools/challenge-md-parser/challengeSeed-to-data.js
Normal file
72
tools/challenge-md-parser/challengeSeed-to-data.js
Normal 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;
|
59
tools/challenge-md-parser/challengeSeed-to-data.test.js
Normal file
59
tools/challenge-md-parser/challengeSeed-to-data.test.js
Normal 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();
|
||||
});
|
||||
});
|
@ -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);
|
||||
|
64
tools/challenge-md-parser/package-lock.json
generated
64
tools/challenge-md-parser/package-lock.json
generated
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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) {
|
||||
|
6
tools/challenge-md-parser/utils/index.js
Normal file
6
tools/challenge-md-parser/utils/index.js
Normal file
@ -0,0 +1,6 @@
|
||||
exports.sectionFilter = (
|
||||
{ type, tagName, properties: { id = '' } },
|
||||
sectionId
|
||||
) => {
|
||||
return type === 'element' && tagName === 'section' && id === sectionId;
|
||||
};
|
Reference in New Issue
Block a user