fix: parse ::directives correctly (#41186)

This commit is contained in:
Oliver Eyton-Williams
2021-02-22 04:44:12 +01:00
committed by GitHub
parent b12360d4a8
commit 04c2f4e620
15 changed files with 842 additions and 21 deletions

View File

@ -287,17 +287,35 @@
}
},
"mdast-util-directive": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-1.0.0.tgz",
"integrity": "sha512-04nOvmHrQfcgPQDAn9x0gW05vhqXmkGPP9G7o8BE+7Oy16oyTAgJpJyVFTscPyEzfsP7p7LSKJAXWqTCBvPjuw==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-1.0.1.tgz",
"integrity": "sha512-VuO1za7BMtWMg8KA8eZrTBorEnCOOW5CXfIuNzUXe7YPie/wLgmNk/jxLMY8m+mzuqnO5eN0JuvlgFtO9EJpbQ==",
"requires": {
"mdast-util-to-markdown": "^0.5.0",
"mdast-util-to-markdown": "^0.6.0",
"parse-entities": "^2.0.0",
"repeat-string": "^1.0.0",
"stringify-entities": "^3.1.0",
"unist-util-visit-parents": "^3.0.0"
},
"dependencies": {
"mdast-util-to-markdown": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-0.6.1.tgz",
"integrity": "sha512-4qJtZ0qdyYeexAXoOZiU0uHIFVncJAmCkHkSluAsvDaVWODtPyNEo9I1ns0T4ulxu2EHRH5u/bt1cV0pdHCX+A==",
"requires": {
"@types/unist": "^2.0.0",
"longest-streak": "^2.0.0",
"mdast-util-to-string": "^2.0.0",
"parse-entities": "^2.0.0",
"repeat-string": "^1.0.0",
"zwitch": "^1.0.0"
}
},
"mdast-util-to-string": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz",
"integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w=="
},
"unist-util-visit-parents": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz",
@ -417,12 +435,23 @@
}
},
"micromark-extension-directive": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-1.0.0.tgz",
"integrity": "sha512-6FjvznI5GpUysZqGtTEMeDaC/D3FdWFVc3CC5gDZB3fBtqiaIRBhCNg4fbqvrFSC0T2eqRbO2dJ7ZFU86gAtEQ==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-1.1.1.tgz",
"integrity": "sha512-xkv00i9brg3LkOMleJc/mlDZlb2TF2TomSS0D4AVhWVDZx4OmuYxFlqtKHuiDXXiVp4mhcZfHsuWdvELdT7I9g==",
"requires": {
"micromark": "~2.10.0",
"micromark": "~2.11.0",
"parse-entities": "^2.0.0"
},
"dependencies": {
"micromark": {
"version": "2.11.2",
"resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.2.tgz",
"integrity": "sha512-IXuP76p2uj8uMg4FQc1cRE7lPCLsfAXuEfdjtdO55VRiFO1asrCSQ5g43NmPqFtRwzEnEhafRVzn2jg0UiKArQ==",
"requires": {
"debug": "^4.0.0",
"parse-entities": "^2.0.0"
}
}
}
},
"micromark-extension-frontmatter": {
@ -568,6 +597,34 @@
"mdast-util-from-markdown": "^0.8.0"
}
},
"remark-stringify": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-9.0.1.tgz",
"integrity": "sha512-mWmNg3ZtESvZS8fv5PTvaPckdL4iNlCHTt8/e/8oN08nArHRHjNZMKzA/YW3+p7/lYqIw4nx1XsjCBo/AxNChg==",
"requires": {
"mdast-util-to-markdown": "^0.6.0"
},
"dependencies": {
"mdast-util-to-markdown": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-0.6.1.tgz",
"integrity": "sha512-4qJtZ0qdyYeexAXoOZiU0uHIFVncJAmCkHkSluAsvDaVWODtPyNEo9I1ns0T4ulxu2EHRH5u/bt1cV0pdHCX+A==",
"requires": {
"@types/unist": "^2.0.0",
"longest-streak": "^2.0.0",
"mdast-util-to-string": "^2.0.0",
"parse-entities": "^2.0.0",
"repeat-string": "^1.0.0",
"zwitch": "^1.0.0"
}
},
"mdast-util-to-string": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz",
"integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w=="
}
}
},
"repeat-string": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",

View File

@ -23,6 +23,7 @@
"remark-frontmatter": "^3.0.0",
"remark-html": "^12.0.0",
"remark-parse": "^9.0.0",
"remark-stringify": "^9.0.1",
"to-vfile": "^5.0.1",
"unified": "^7.0.0",
"unist-util-find": "^1.0.1",

View File

@ -0,0 +1,185 @@
{
"type": "root",
"children": [
{
"type": "heading",
"depth": 1,
"children": [
{
"type": "text",
"value": "--description--",
"position": {
"start": { "line": 1, "column": 3, "offset": 2 },
"end": { "line": 1, "column": 18, "offset": 17 }
}
}
],
"position": {
"start": { "line": 1, "column": 1, "offset": 0 },
"end": { "line": 1, "column": 18, "offset": 17 }
}
},
{
"type": "paragraph",
"children": [
{
"type": "textDirective",
"name": "root",
"attributes": {},
"children": [],
"position": {
"start": { "line": 3, "column": 1, "offset": 19 },
"end": { "line": 3, "column": 6, "offset": 24 }
}
},
{
"type": "text",
"value": " appears, ",
"position": {
"start": { "line": 3, "column": 6, "offset": 24 },
"end": { "line": 3, "column": 16, "offset": 34 }
}
},
{
"type": "textDirective",
"name": "import",
"attributes": {},
"children": [],
"position": {
"start": { "line": 3, "column": 16, "offset": 34 },
"end": { "line": 3, "column": 23, "offset": 41 }
}
},
{
"type": "text",
"value": " appears",
"position": {
"start": { "line": 3, "column": 23, "offset": 41 },
"end": { "line": 3, "column": 31, "offset": 49 }
}
}
],
"position": {
"start": { "line": 3, "column": 1, "offset": 19 },
"end": { "line": 3, "column": 31, "offset": 49 }
}
},
{
"type": "paragraph",
"children": [
{
"type": "text",
"value": "the next paragraph should appear",
"position": {
"start": { "line": 5, "column": 1, "offset": 51 },
"end": { "line": 5, "column": 33, "offset": 83 }
}
}
],
"position": {
"start": { "line": 5, "column": 1, "offset": 51 },
"end": { "line": 5, "column": 33, "offset": 83 }
}
},
{
"type": "leafDirective",
"name": "import",
"attributes": {},
"children": [],
"position": {
"start": { "line": 7, "column": 1, "offset": 85 },
"end": { "line": 7, "column": 9, "offset": 93 }
}
},
{
"type": "paragraph",
"children": [
{
"type": "text",
"value": "even though it's an import directive, but if we use the full syntax ",
"position": {
"start": { "line": 9, "column": 1, "offset": 95 },
"end": { "line": 9, "column": 58, "offset": 152 }
}
},
{
"type": "inlineCode",
"value": "::directive-name{attr=\"name\" attr2=\"a/path\"}",
"position": {
"start": { "line": 9, "column": 58, "offset": 152 },
"end": { "line": 9, "column": 100, "offset": 194 }
}
}
],
"position": {
"start": { "line": 9, "column": 1, "offset": 95 },
"end": { "line": 9, "column": 100, "offset": 194 }
}
},
{
"type": "leafDirective",
"name": "import",
"attributes": { "component": "name", "from": "script.md" },
"children": [],
"position": {
"start": { "line": 11, "column": 1, "offset": 196 },
"end": { "line": 11, "column": 44, "offset": 239 }
}
},
{
"type": "paragraph",
"children": [
{
"type": "text",
"value": "it goes.",
"position": {
"start": { "line": 13, "column": 1, "offset": 241 },
"end": { "line": 13, "column": 9, "offset": 249 }
}
}
],
"position": {
"start": { "line": 13, "column": 1, "offset": 241 },
"end": { "line": 13, "column": 9, "offset": 249 }
}
},
{
"type": "paragraph",
"children": [
{
"type": "text",
"value": "::: name [inline-content] {key=val}\na container directive\n:::",
"position": {
"start": { "line": 15, "column": 1, "offset": 251 },
"end": { "line": 17, "column": 4, "offset": 312 }
}
}
],
"position": {
"start": { "line": 15, "column": 1, "offset": 251 },
"end": { "line": 17, "column": 4, "offset": 312 }
}
},
{
"type": "paragraph",
"children": [
{
"type": "text",
"value": ":::",
"position": {
"start": { "line": 19, "column": 1, "offset": 314 },
"end": { "line": 19, "column": 4, "offset": 317 }
}
}
],
"position": {
"start": { "line": 19, "column": 1, "offset": 314 },
"end": { "line": 19, "column": 4, "offset": 317 }
}
}
],
"position": {
"start": { "line": 1, "column": 1, "offset": 0 },
"end": { "line": 20, "column": 1, "offset": 318 }
}
}

View File

@ -0,0 +1,321 @@
{
"type": "root",
"children": [
{
"type": "leafDirective",
"name": "import",
"attributes": { "component": "Script", "from": "./script.md" },
"children": [],
"position": {
"start": { "line": 1, "column": 1, "offset": 0 },
"end": { "line": 1, "column": 49, "offset": 48 }
}
},
{
"type": "leafDirective",
"name": "import",
"attributes": { "component": "Second", "from": "./script-two.md" },
"children": [],
"position": {
"start": { "line": 2, "column": 1, "offset": 49 },
"end": { "line": 2, "column": 53, "offset": 101 }
}
},
{
"type": "leafDirective",
"name": "import",
"attributes": {},
"children": [],
"position": {
"start": { "line": 3, "column": 1, "offset": 102 },
"end": { "line": 3, "column": 9, "offset": 110 }
}
},
{
"type": "heading",
"depth": 1,
"children": [
{
"type": "text",
"value": "--description--",
"position": {
"start": { "line": 5, "column": 3, "offset": 114 },
"end": { "line": 5, "column": 18, "offset": 129 }
}
}
],
"position": {
"start": { "line": 5, "column": 1, "offset": 112 },
"end": { "line": 5, "column": 18, "offset": 129 }
}
},
{
"type": "paragraph",
"children": [
{
"type": "text",
"value": "Paragraph 1 ::import",
"position": {
"start": { "line": 7, "column": 1, "offset": 131 },
"end": { "line": 7, "column": 21, "offset": 151 }
}
}
],
"position": {
"start": { "line": 7, "column": 1, "offset": 131 },
"end": { "line": 7, "column": 21, "offset": 151 }
}
},
{
"type": "code",
"lang": "html",
"meta": null,
"value": "code example",
"position": {
"start": { "line": 9, "column": 1, "offset": 153 },
"end": { "line": 11, "column": 4, "offset": 177 }
}
},
{
"type": "heading",
"depth": 1,
"children": [
{
"type": "text",
"value": "--instructions--",
"position": {
"start": { "line": 13, "column": 3, "offset": 181 },
"end": { "line": 13, "column": 19, "offset": 197 }
}
}
],
"position": {
"start": { "line": 13, "column": 1, "offset": 179 },
"end": { "line": 13, "column": 19, "offset": 197 }
}
},
{
"type": "paragraph",
"children": [
{
"type": "text",
"value": "Paragraph 0",
"position": {
"start": { "line": 15, "column": 1, "offset": 199 },
"end": { "line": 15, "column": 12, "offset": 210 }
}
}
],
"position": {
"start": { "line": 15, "column": 1, "offset": 199 },
"end": { "line": 15, "column": 12, "offset": 210 }
}
},
{
"type": "code",
"lang": "html",
"meta": null,
"value": "code example 0",
"position": {
"start": { "line": 17, "column": 1, "offset": 212 },
"end": { "line": 19, "column": 4, "offset": 238 }
}
},
{
"type": "heading",
"depth": 1,
"children": [
{
"type": "text",
"value": "--hints--",
"position": {
"start": { "line": 21, "column": 3, "offset": 242 },
"end": { "line": 21, "column": 12, "offset": 251 }
}
}
],
"position": {
"start": { "line": 21, "column": 1, "offset": 240 },
"end": { "line": 21, "column": 12, "offset": 251 }
}
},
{
"type": "paragraph",
"children": [
{
"type": "text",
"value": "First hint",
"position": {
"start": { "line": 23, "column": 1, "offset": 253 },
"end": { "line": 23, "column": 11, "offset": 263 }
}
}
],
"position": {
"start": { "line": 23, "column": 1, "offset": 253 },
"end": { "line": 23, "column": 11, "offset": 263 }
}
},
{
"type": "code",
"lang": "js",
"meta": null,
"value": "// test code",
"position": {
"start": { "line": 25, "column": 1, "offset": 265 },
"end": { "line": 27, "column": 4, "offset": 287 }
}
},
{
"type": "paragraph",
"children": [
{
"type": "text",
"value": "Second hint with ",
"position": {
"start": { "line": 29, "column": 1, "offset": 289 },
"end": { "line": 29, "column": 18, "offset": 306 }
}
},
{
"type": "html",
"value": "<code>",
"position": {
"start": { "line": 29, "column": 18, "offset": 306 },
"end": { "line": 29, "column": 24, "offset": 312 }
}
},
{
"type": "text",
"value": "code",
"position": {
"start": { "line": 29, "column": 24, "offset": 312 },
"end": { "line": 29, "column": 28, "offset": 316 }
}
},
{
"type": "html",
"value": "</code>",
"position": {
"start": { "line": 29, "column": 28, "offset": 316 },
"end": { "line": 29, "column": 35, "offset": 323 }
}
}
],
"position": {
"start": { "line": 29, "column": 1, "offset": 289 },
"end": { "line": 29, "column": 35, "offset": 323 }
}
},
{
"type": "code",
"lang": "js",
"meta": null,
"value": "// more test code",
"position": {
"start": { "line": 31, "column": 1, "offset": 325 },
"end": { "line": 33, "column": 4, "offset": 352 }
}
},
{
"type": "heading",
"depth": 1,
"children": [
{
"type": "text",
"value": "--seed--",
"position": {
"start": { "line": 36, "column": 3, "offset": 357 },
"end": { "line": 36, "column": 11, "offset": 365 }
}
}
],
"position": {
"start": { "line": 36, "column": 1, "offset": 355 },
"end": { "line": 36, "column": 11, "offset": 365 }
}
},
{
"type": "heading",
"depth": 2,
"children": [
{
"type": "text",
"value": "--seed-contents--",
"position": {
"start": { "line": 38, "column": 4, "offset": 370 },
"end": { "line": 38, "column": 21, "offset": 387 }
}
}
],
"position": {
"start": { "line": 38, "column": 1, "offset": 367 },
"end": { "line": 38, "column": 21, "offset": 387 }
}
},
{
"type": "code",
"lang": "html",
"meta": null,
"value": "<html>\n <body>\n </body>\n</html>",
"position": {
"start": { "line": 40, "column": 1, "offset": 389 },
"end": { "line": 45, "column": 4, "offset": 434 }
}
},
{
"type": "code",
"lang": "css",
"meta": null,
"value": "body {\n background: green;\n}",
"position": {
"start": { "line": 47, "column": 1, "offset": 436 },
"end": { "line": 51, "column": 4, "offset": 476 }
}
},
{
"type": "leafDirective",
"name": "use",
"attributes": { "component": "Second" },
"children": [],
"position": {
"start": { "line": 53, "column": 1, "offset": 478 },
"end": { "line": 53, "column": 26, "offset": 503 }
}
},
{
"type": "leafDirective",
"name": "id",
"attributes": { "id": "custom-name" },
"children": [],
"position": {
"start": { "line": 55, "column": 1, "offset": 505 },
"end": { "line": 55, "column": 19, "offset": 523 }
}
},
{
"type": "code",
"lang": "js",
"meta": null,
"value": "var x = 'y';",
"position": {
"start": { "line": 57, "column": 1, "offset": 525 },
"end": { "line": 59, "column": 4, "offset": 547 }
}
},
{
"type": "leafDirective",
"name": "use",
"attributes": { "component": "Script" },
"children": [],
"position": {
"start": { "line": 61, "column": 1, "offset": 549 },
"end": { "line": 61, "column": 26, "offset": 574 }
}
}
],
"position": {
"start": { "line": 1, "column": 1, "offset": 0 },
"end": { "line": 62, "column": 1, "offset": 575 }
}
}

View File

@ -0,0 +1,17 @@
# --description--
:root appears, :import appears
the next paragraph should appear
::import
even though it's an import directive, but if we use the full syntax `::directive-name{attr="name" attr2="a/path"}`
::import{component="name" from="script.md"}
it goes.
::: name [inline-content] {key=val}
a container directive
:::

View File

@ -0,0 +1,61 @@
::import{component="Script" from="./script.md" }
::import{component="Second" from="./script-two.md" }
::import
# --description--
Paragraph 1 ::import
```html
code example
```
# --instructions--
Paragraph 0
```html
code example 0
```
# --hints--
First hint
```js
// test code
```
Second hint with <code>code</code>
```js
// more test code
```
# --seed--
## --seed-contents--
```html
<html>
<body>
</body>
</html>
```
```css
body {
background: green;
}
```
::use{component="Second"}
::id{#custom-name}
```js
var x = 'y';
```
::use{component="Script"}

View File

@ -1,5 +1,24 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`challenge parser it should not parse directives we do not use 1`] = `
Object {
"description": "<section id=\\"description\\">
<p>:root appears, :import appears</p>
<p>the next paragraph should appear</p>
::import
<p>even though it's an import directive, but if we use the full syntax <code>::directive-name{attr=\\"name\\" attr2=\\"a/path\\"}</code></p>
<p>it goes.</p>
<p>::: name [inline-content] {key=val}
a container directive
:::</p>
</section>",
"solutions": Array [
Object {},
],
"tests": Array [],
}
`;
exports[`challenge parser it should parse video questions 1`] = `
Object {
"description": "<section id=\\"description\\">

View File

@ -53,4 +53,11 @@ describe('challenge parser', () => {
);
expect(parsed).toMatchSnapshot();
});
it('it should not parse directives we do not use', async () => {
const parsed = await parseMD(
path.resolve(__dirname, '__fixtures__/with-directives.md')
);
expect(parsed).toMatchSnapshot();
});
});

View File

@ -3,6 +3,7 @@ const remark = require('remark-parse');
const directive = require('remark-directive');
const frontmatter = require('remark-frontmatter');
const addTests = require('./plugins/add-tests');
const restoreDirectives = require('./plugins/restore-directives');
const replaceImports = require('./plugins/replace-imports');
const addFrontmatter = require('./plugins/add-frontmatter');
const addText = require('./plugins/add-text');
@ -33,14 +34,18 @@ const processor = unified()
// ::use{component="Script"}
// appears.
.use(replaceImports)
// the final five plugins insert content into file.data
.use(addText, ['description', 'instructions'])
// the final five 'add' plugins insert content into file.data
// TODO: rename test->hint everywhere? It should make things easier to reason
// about.
.use(addTests)
.use(addVideoQuestion)
.use(addSeed)
.use(addSolution);
.use(addSolution)
// the directives will have been parsed and used by this point, any remaining
// 'directives' will be from text like the css selector :root. These should be
// converted back to text before they're added to the challenge object.
.use(restoreDirectives)
.use(addVideoQuestion)
.use(addTests)
.use(addText, ['description', 'instructions']);
exports.parseMD = function parseMD(filename) {
return new Promise((resolve, reject) => {

View File

@ -31,6 +31,10 @@ function plugin() {
}
const importPromises = importedFiles.map(async ({ attributes }) => {
const { from, component } = attributes;
// if these are missing, bail, since it's not an import.
if (!from || !component) {
return null;
}
const location = path.resolve(file.dirname, from);
return await read(location)
.then(parse)
@ -65,13 +69,27 @@ function plugin() {
// Also, we remove the import statements here.
Promise.all(importPromises)
.then(() => {
remove(tree, { type: 'leafDirective', name: 'import' });
remove(tree, isImportNode);
next();
})
.catch(next);
.catch(err => {
console.error('error processing ::import');
console.error(err);
next(err);
});
}
}
function isImportNode({ type, name, attributes }) {
if (!attributes) return false;
return (
type === 'leafDirective' &&
name === 'import' &&
attributes.component &&
attributes.from
);
}
function validateImports(fileTree) {
let valid = true;

View File

@ -1,4 +1,4 @@
/* global describe it expect */
/* global describe it expect jest */
const path = require('path');
const cloneDeep = require('lodash/cloneDeep');
const toVfile = require('to-vfile');
@ -7,12 +7,14 @@ const selectAll = require('unist-util-select').selectAll;
const addImports = require('./replace-imports');
const originalImportsAST = require('../__fixtures__/ast-imports.json');
const originalImportsTwoAST = require('../__fixtures__/ast-imports-two.json');
const originalImportsExtraAST = require('../__fixtures__/ast-imports-extra.json');
const originalSimpleAST = require('../__fixtures__/ast-simple.json');
const originalMarkerAST = require('../__fixtures__/ast-marker-imports.json');
describe('replace-imports', () => {
let importsAST;
let importsTwoAST;
let importsExtraAST;
let simpleAST;
let markerAST;
let correctFile;
@ -21,6 +23,7 @@ describe('replace-imports', () => {
beforeEach(() => {
importsAST = cloneDeep(originalImportsAST);
importsTwoAST = cloneDeep(originalImportsTwoAST);
importsExtraAST = cloneDeep(originalImportsExtraAST);
simpleAST = cloneDeep(originalSimpleAST);
markerAST = cloneDeep(originalMarkerAST);
correctFile = toVfile(
@ -56,12 +59,15 @@ describe('replace-imports', () => {
});
it('should fail when the imported file cannot be found', done => {
expect.assertions(1);
console.error = jest.fn();
const plugin = addImports();
// we have to rely on the next callback, because that is how you get error
// messages out of transformers
const next = err => {
if (err) {
expect(console.error).toHaveBeenCalledTimes(2);
done();
} else {
done('An error should have been thrown by addImports');
@ -118,6 +124,26 @@ describe('replace-imports', () => {
plugin(importsAST, correctFile, next);
});
it('should not remove an ::import without the required attributes', done => {
expect.assertions(2);
const selector = 'leafDirective[name=import]';
const plugin = addImports();
const importNodes = selectAll(selector, importsExtraAST);
expect(importNodes.length).toBe(3);
const next = err => {
if (err) {
done(err);
} else {
const importNodes = selectAll(selector, importsExtraAST);
expect(importNodes.length).toBe(1);
done();
}
};
plugin(importsExtraAST, correctFile, next);
});
it('should remove all matching ::use statements', done => {
expect.assertions(2);
const selector = 'leafDirective[name=use]';
@ -205,9 +231,12 @@ describe('replace-imports', () => {
});
it('should reject imported files with editable region markers', done => {
expect.assertions(1);
console.error = jest.fn();
const plugin = addImports();
const next = err => {
if (err) {
expect(console.error).toHaveBeenCalledTimes(2);
done();
} else {
done('An error should have been thrown by addImports');

View File

@ -0,0 +1,32 @@
const visit = require('unist-util-visit');
const { matches } = require('unist-util-select');
const directive = require('mdast-util-directive');
var toMarkdown = require('mdast-util-to-markdown');
function plugin() {
return transformer;
function transformer(tree) {
visit(tree, visitor);
function visitor(node, id, parent) {
// currently `remark-directive` seems to be ignoring containerDirectives
// but, assuming that will get fixed, we test for it anyway.
const isDirective =
matches('leafDirective', node) ||
matches('textDirective', node) ||
matches('containerDirective', node);
if (isDirective) {
parent.children[id] = {
type: 'text',
value: toMarkdown(node, {
extensions: [directive.toMarkdown]
}).trim()
};
}
}
}
}
module.exports = plugin;

View File

@ -0,0 +1,66 @@
/* global describe it expect */
const cloneDeep = require('lodash/cloneDeep');
const { selectAll } = require('unist-util-select');
const find = require('unist-util-find');
const restoreDirectives = require('./restore-directives');
const directivesOriginalAST = require('../__fixtures__/ast-directives.json');
describe('restore-directives', () => {
let directivesAST;
beforeEach(() => {
directivesAST = cloneDeep(directivesOriginalAST);
});
it('should return a function', () => {
expect.assertions(1);
const plugin = restoreDirectives();
expect(typeof plugin).toEqual('function');
});
// TODO: if remark-directive starts processing containers, add them to the
// tests
it('should remove any directives in the AST', () => {
expect.assertions(4);
const plugin = restoreDirectives();
let leaves = selectAll('leafDirective', directivesAST);
let text = selectAll('textDirective', directivesAST);
expect(leaves.length).toBe(2);
expect(text.length).toBe(2);
plugin(directivesAST);
leaves = selectAll('leafDirective', directivesAST);
text = selectAll('textDirective', directivesAST);
expect(leaves.length).toBe(0);
expect(text.length).toBe(0);
});
it('should put the original text into the AST', () => {
expect.assertions(4);
const plugin = restoreDirectives();
let nodeWithImport = find(
directivesAST,
node => node.value && node.value.includes('::import')
);
let nodeWithRoot = find(
directivesAST,
node => node.value && node.value.includes(':root')
);
expect(nodeWithImport).not.toBeTruthy();
expect(nodeWithRoot).not.toBeTruthy();
plugin(directivesAST);
nodeWithImport = find(
directivesAST,
node => node.value && node.value.includes('::import')
);
nodeWithRoot = find(
directivesAST,
node => node.value && node.value.includes(':root')
);
expect(nodeWithImport).toBeTruthy();
expect(nodeWithRoot).toBeTruthy();
});
});

View File

@ -1,10 +1,10 @@
const { inspect } = require('util');
const path = require('path');
const { parsemd } = require('../index');
const { parseMD } = require('../index');
(async () => {
const fullPath = path.resolve(__dirname, '../__fixtures__/realistic.md');
const output = await parsemd(fullPath);
const fullPath = path.resolve(__dirname, './example.md');
const output = await parseMD(fullPath);
console.log(inspect(output, null, null, true));
})();

View File

@ -1,12 +1,15 @@
const { read } = require('to-vfile');
const remark = require('remark');
const html = require('remark-html');
const directives = require('remark-directive');
const stringify = require('remark-stringify');
(async () => {
const path = './example.md';
const file = await read(path);
const contents = await remark()
.use(html)
.use(directives)
.use(() => tree => console.dir(tree, { depth: null, colors: true }))
.use(stringify)
.process(file);
console.log(contents);
})();