fix: parse ::directives correctly (#41186)
This commit is contained in:
committed by
GitHub
parent
b12360d4a8
commit
04c2f4e620
73
tools/challenge-parser/package-lock.json
generated
73
tools/challenge-parser/package-lock.json
generated
@ -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",
|
||||
|
@ -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",
|
||||
|
185
tools/challenge-parser/parser/__fixtures__/ast-directives.json
Normal file
185
tools/challenge-parser/parser/__fixtures__/ast-directives.json
Normal 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 }
|
||||
}
|
||||
}
|
@ -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 }
|
||||
}
|
||||
}
|
@ -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
|
||||
:::
|
@ -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"}
|
@ -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\\">
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
@ -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) => {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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');
|
||||
|
32
tools/challenge-parser/parser/plugins/restore-directives.js
Normal file
32
tools/challenge-parser/parser/plugins/restore-directives.js
Normal 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;
|
@ -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();
|
||||
});
|
||||
});
|
@ -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));
|
||||
})();
|
||||
|
@ -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);
|
||||
})();
|
||||
|
Reference in New Issue
Block a user