van-mdx 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +97 -0
- package/package.json +27 -0
- package/src/bundlers/vite.js +15 -0
- package/src/index.js +0 -0
- package/src/transpiler/index.js +2 -0
- package/src/transpiler/process.js +168 -0
- package/src/transpiler/transpile.js +23 -0
- package/src/utils/component-type.js +5 -0
- package/src/utils/hyperscript.js +30 -0
- package/src/utils/index.js +4 -0
- package/src/utils/parse-yml.js +23 -0
- package/src/utils/process-attributes.js +13 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 ZAKARIA ELALAOUI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# van-mdx
|
|
2
|
+
|
|
3
|
+
A Markdown preprocessor for [Vanjs](https://vanjs.org/).
|
|
4
|
+
It combines the simplicity of Markdown syntax with the power and flexibility of ***Javascript***
|
|
5
|
+
|
|
6
|
+
## Demos :
|
|
7
|
+
|
|
8
|
+
## Install :
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm i van-mdx@latest
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Config :
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
import {defineConfig} from "vite"
|
|
18
|
+
import VanMdx from "van-mdx/vite"
|
|
19
|
+
export default defineConfig({
|
|
20
|
+
plugins : [
|
|
21
|
+
VanMdx()
|
|
22
|
+
]
|
|
23
|
+
})
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage :
|
|
27
|
+
|
|
28
|
+
- ***Article.mdx :***
|
|
29
|
+
```jsx
|
|
30
|
+
---
|
|
31
|
+
title : Van-Mdx Starter
|
|
32
|
+
name : world
|
|
33
|
+
__props__ :
|
|
34
|
+
background : tomato
|
|
35
|
+
data : []
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
import data from "./data.js";
|
|
39
|
+
import InteractiveComponent from "./InteractiveComponent.js";
|
|
40
|
+
|
|
41
|
+
# Hello {name}
|
|
42
|
+
|
|
43
|
+
<InteractiveComponent data={data} background={tomato}/>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
// main.js
|
|
48
|
+
import van from "vanjs-core"
|
|
49
|
+
import InteractiveArticle,{title} from "./Article.mdx"
|
|
50
|
+
|
|
51
|
+
const {article} = van.tags;
|
|
52
|
+
|
|
53
|
+
const Article_1 = article(
|
|
54
|
+
InteractiveArticle({
|
|
55
|
+
background : "yellow"
|
|
56
|
+
})
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
van.add(
|
|
60
|
+
Article_1
|
|
61
|
+
)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Features :
|
|
65
|
+
|
|
66
|
+
- ***Simple Integration :*** Write Markdown as usual, and inject Vanjs components wherever needed.
|
|
67
|
+
- ***Extensible :*** Create custom interactive components using `Vanjs` and use them in any Markdown file.
|
|
68
|
+
- ***Reusable :*** `Van-Mdx` exports a default functional component, allowing you to call it multiple times with different data, enabling dynamic and versatile use.
|
|
69
|
+
- ***Frontmatter Support :*** Use `YAML` syntax in to include metadata like titles, descriptions, or configurations in your Markdown files, and define props to pass data dynamically to Zikojs components.
|
|
70
|
+
- ***Markdown Support :*** Use standard Markdown syntax for writing content.
|
|
71
|
+
- ***HTML Support :*** Use standard HTML syntax for writing content.
|
|
72
|
+
- ***JSX Syntax :*** Declare component using Vanjs Hyperscript syntax, and render it using JSX
|
|
73
|
+
- ***Props :*** Pass data to components through props, enabling dynamic rendering and customization of content within your Markdown files.
|
|
74
|
+
- ***Attributes:***
|
|
75
|
+
- ***ESM : :*** Supports ECMAScript Modules (ESM), allowing you to import and export modules
|
|
76
|
+
- ***Expressions :*** Van-Mdx lets you use JS expressions inside curly braces, like Hello {name}. These expressions can be full JS programs, as long as they evaluate to something renderable. For example, you can use an IIFE like this:
|
|
77
|
+
```js
|
|
78
|
+
Hello {(()=>{
|
|
79
|
+
const names = ["world", "everyone"];
|
|
80
|
+
const {length} = names
|
|
81
|
+
return names[Math.floor(Math.random()*length)]
|
|
82
|
+
})()}
|
|
83
|
+
```
|
|
84
|
+
- ***Internal scripts :*** Include JS logic that runs alongside Van-Mdx components but isn't rendered in the output. They can initialize variables or perform side effects...
|
|
85
|
+
- ***Interleaving :*** You can use inline markdown elements inside HTML or Vanjs Components
|
|
86
|
+
```jsx
|
|
87
|
+
<p>
|
|
88
|
+
***Hello {name}***
|
|
89
|
+
</p>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# ⭐️ Show your support
|
|
94
|
+
If you appreciate the project, kindly demonstrate your support by giving it a star!
|
|
95
|
+
|
|
96
|
+
# Licence
|
|
97
|
+
This projet is licensed under the terms of MIT License
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "van-mdx",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "Markdown for Vanjs",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./src/index.js",
|
|
12
|
+
"./vite": "./src/bundlers/vite.js"
|
|
13
|
+
},
|
|
14
|
+
"author": "zakaria elalaoui",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"highlight.js": "^11.11.1",
|
|
18
|
+
"mdzjs": "^0.0.8",
|
|
19
|
+
"remark-frontmatter": "^5.0.0",
|
|
20
|
+
"remark-gfm": "^4.0.1",
|
|
21
|
+
"remark-mdx": "^3.1.0",
|
|
22
|
+
"remark-parse": "^11.0.0",
|
|
23
|
+
"unified": "^11.0.5",
|
|
24
|
+
"vanjs-core": "^1.5.5",
|
|
25
|
+
"ziko": "^0.0.27"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { transpileMD } from "../transpiler/index.js";
|
|
2
|
+
export default function VanMdx(){
|
|
3
|
+
return {
|
|
4
|
+
name: 'VanMdx',
|
|
5
|
+
transform(src, id) {
|
|
6
|
+
if (id.endsWith('.mdx')) {
|
|
7
|
+
return {
|
|
8
|
+
code: transpileMD(src),
|
|
9
|
+
map: null,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
package/src/index.js
ADDED
|
File without changes
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import {
|
|
2
|
+
componentType,
|
|
3
|
+
processAttribute,
|
|
4
|
+
parseYml,
|
|
5
|
+
hyperscript
|
|
6
|
+
} from "../utils/index.js"
|
|
7
|
+
import hljs from "highlight.js"
|
|
8
|
+
const processMDAST = (markdownAST) => {
|
|
9
|
+
let hasCode = false;
|
|
10
|
+
const transformNode = (node) => {
|
|
11
|
+
switch(node.type){
|
|
12
|
+
case 'mdxjsEsm' : {
|
|
13
|
+
return {
|
|
14
|
+
type : "script",
|
|
15
|
+
value : node.value
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
case 'text' : {
|
|
19
|
+
const text = node.value;
|
|
20
|
+
const escaped = text.replace(/"/g, '\\"');
|
|
21
|
+
return `"${escaped}"`;
|
|
22
|
+
};
|
|
23
|
+
case 'mdxTextExpression' : {
|
|
24
|
+
const {value} = node
|
|
25
|
+
return value
|
|
26
|
+
};
|
|
27
|
+
case 'heading' : {
|
|
28
|
+
const childNodes = node.children.map(transformNode).join(', ');
|
|
29
|
+
return hyperscript(`h${node.depth}`,"{}", childNodes);
|
|
30
|
+
};
|
|
31
|
+
case 'paragraph' : {
|
|
32
|
+
const childNodes = node.children.map(transformNode).join(', ');
|
|
33
|
+
return hyperscript("p","{}", childNodes)
|
|
34
|
+
};
|
|
35
|
+
case 'strong': {
|
|
36
|
+
const childNodes = node.children.map(transformNode).join(', ');
|
|
37
|
+
return hyperscript("strong","{}", childNodes);
|
|
38
|
+
};
|
|
39
|
+
case 'emphasis': {
|
|
40
|
+
const childNodes = node.children.map(transformNode).join(', ');
|
|
41
|
+
return hyperscript("em","{}", childNodes);
|
|
42
|
+
};
|
|
43
|
+
case 'link': {
|
|
44
|
+
const childNodes = node.children.map(transformNode).join(', ');
|
|
45
|
+
return hyperscript("a", `{ href: "${node.url}" }`, childNodes);
|
|
46
|
+
};
|
|
47
|
+
case 'image': {
|
|
48
|
+
hyperscript("img", `{ src: "${node.url}", alt: "${node.alt || ''}`)
|
|
49
|
+
return `h('img', { src: "${node.url}", alt: "${node.alt || ''}" })`;
|
|
50
|
+
};
|
|
51
|
+
case 'list': {
|
|
52
|
+
const listTag = node.ordered ? 'ol' : 'ul';
|
|
53
|
+
const childNodes = node.children.map(transformNode).join(', ');
|
|
54
|
+
return hyperscript(listTag, "{}", childNodes);
|
|
55
|
+
};
|
|
56
|
+
case 'listItem': {
|
|
57
|
+
const childNodes = node.children.map(transformNode).join(', ');
|
|
58
|
+
return hyperscript("li", "{}", childNodes);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
case 'code': {
|
|
62
|
+
hasCode = true;
|
|
63
|
+
// const language = node.lang ? `{ 'data-lang': '${node.lang}' }` : '';
|
|
64
|
+
const highlightedCode = hljs.highlightAuto(node.value, [node.lang || '']).value;
|
|
65
|
+
const formatedCode = highlightedCode.replace(/(\r\n|\n|\r)/g, "<br>")
|
|
66
|
+
return `HTMLWrapper('<pre>${formatedCode}</pre>').element`
|
|
67
|
+
}
|
|
68
|
+
case 'blockquote': {
|
|
69
|
+
const childNodes = node.children.map(transformNode).join(', ');
|
|
70
|
+
return hyperscript("blockquote", "{}", childNodes);
|
|
71
|
+
}
|
|
72
|
+
case 'thematicBreak': {
|
|
73
|
+
return `van.tags.hr()`;
|
|
74
|
+
}
|
|
75
|
+
case 'table': {
|
|
76
|
+
const headerRows = node.children[0].children.map(transformNode).join(', ');
|
|
77
|
+
const bodyRows = node.children.slice(1).map(transformNode).join(', ');
|
|
78
|
+
const thead = hyperscript("thead", "{}", hyperscript("tr", "{}", headerRows));
|
|
79
|
+
const tbody = hyperscript("tbody", "{}", bodyRows);
|
|
80
|
+
return hyperscript("table", "{}", [thead, tbody].join(","))
|
|
81
|
+
// console.log({thead, tbody})
|
|
82
|
+
// const thead = `h('thead', {}, h('tr', {}, ${headerRows})`;
|
|
83
|
+
// const tbody = `h('tbody', {}, ${bodyRows})`
|
|
84
|
+
// return `h('table', {}, ${thead}), ${tbody}).style({border : "1px solid darkblue", borderCollapse: "collapse"}`;
|
|
85
|
+
}
|
|
86
|
+
case 'tableRow': {
|
|
87
|
+
const cells = node.children.map(transformNode).join(', ');
|
|
88
|
+
return `${hyperscript("tr", "{}", cells)}`
|
|
89
|
+
return `${hyperscript("tr", "{}", cells)}.style({border : "1px solid darkblue", borderCollapse: "collapse"})`
|
|
90
|
+
}
|
|
91
|
+
case 'tableCell': {
|
|
92
|
+
const childNodes = node.children.map(transformNode).join(', ');
|
|
93
|
+
return `${hyperscript("td", "{}", childNodes)}`
|
|
94
|
+
return `${hyperscript("td", "{}", childNodes)}.style({border : "1px solid darkblue", borderCollapse: "collapse", padding : "5px"})`
|
|
95
|
+
}
|
|
96
|
+
case 'yaml':{
|
|
97
|
+
const {props, attrs} = parseYml(node.value)
|
|
98
|
+
return {
|
|
99
|
+
type : "yaml",
|
|
100
|
+
props,
|
|
101
|
+
attrs
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
case 'mdxJsxTextElement': {
|
|
105
|
+
const {name, attributes, children} = node;
|
|
106
|
+
const childNodes = children.map(transformNode).join(', ');
|
|
107
|
+
const hasChildren = childNodes.length > 0;
|
|
108
|
+
return `van.tags.${name}(${processAttribute(attributes)}${hasChildren ?`, ${childNodes}`:""})`;
|
|
109
|
+
};
|
|
110
|
+
case 'mdxJsxFlowElement':{
|
|
111
|
+
const {name, attributes, children} = node;
|
|
112
|
+
const childNodes = children.map(transformNode).join(', ');
|
|
113
|
+
const hasChildren = childNodes.length > 0;
|
|
114
|
+
switch(componentType(name)){
|
|
115
|
+
case "jsx" : {
|
|
116
|
+
return `${name}(${processAttribute(attributes)}${hasChildren ?`, ${childNodes}`:""})`;
|
|
117
|
+
}
|
|
118
|
+
case "html" : {
|
|
119
|
+
return `van.tags.${name}(${processAttribute(attributes)}${hasChildren ?`, ${childNodes}`:""})`;
|
|
120
|
+
}
|
|
121
|
+
case "script" : {
|
|
122
|
+
const statements = [];
|
|
123
|
+
for(let i=0; i<node.children.length; i++) statements.push(node.children[i].children[0].value)
|
|
124
|
+
return {
|
|
125
|
+
type : "script",
|
|
126
|
+
isScript : true,
|
|
127
|
+
value : statements.join("\n")
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
default : {
|
|
133
|
+
console.log(node.type)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return 'null';
|
|
137
|
+
};
|
|
138
|
+
let esm = [];
|
|
139
|
+
let props = "";
|
|
140
|
+
let attrs = "";
|
|
141
|
+
|
|
142
|
+
const statements = []
|
|
143
|
+
markdownAST.children.forEach((node) => {
|
|
144
|
+
switch(node.type){
|
|
145
|
+
case 'yaml' : {
|
|
146
|
+
const Transformed = transformNode(node)
|
|
147
|
+
props = Transformed.props;
|
|
148
|
+
attrs = Transformed.attrs;
|
|
149
|
+
} break;
|
|
150
|
+
case 'mdxjsEsm' : esm.push(node.value); break;
|
|
151
|
+
default : {
|
|
152
|
+
const Transformed = transformNode(node);
|
|
153
|
+
if(Transformed.isScript) statements.push(Transformed.value);
|
|
154
|
+
else statements.push(`__items__.push(${Transformed})`)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
return {
|
|
159
|
+
attrs,
|
|
160
|
+
props,
|
|
161
|
+
esm,
|
|
162
|
+
statements,
|
|
163
|
+
hasCode
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
export {
|
|
167
|
+
processMDAST
|
|
168
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { parseMarkdown } from "mdzjs";
|
|
2
|
+
import { processMDAST } from "./process.js";
|
|
3
|
+
import { stringifyProps } from "../utils/parse-yml.js";
|
|
4
|
+
|
|
5
|
+
const transpileMD=(Markdown)=>{
|
|
6
|
+
const ast = parseMarkdown(Markdown);
|
|
7
|
+
const {attrs, props, esm, statements, hasCode}= processMDAST(ast)
|
|
8
|
+
const body = [
|
|
9
|
+
'import van from "vanjs-core"',
|
|
10
|
+
'import { HTMLWrapper } from "ziko"',
|
|
11
|
+
attrs,
|
|
12
|
+
...esm,
|
|
13
|
+
`export default (${stringifyProps(props)})=>{`,
|
|
14
|
+
'const __items__ = []',
|
|
15
|
+
...statements,
|
|
16
|
+
"const UI = van.tags.div(...__items__)",
|
|
17
|
+
"return UI }"
|
|
18
|
+
]
|
|
19
|
+
return body.join("\n");
|
|
20
|
+
}
|
|
21
|
+
export{
|
|
22
|
+
transpileMD
|
|
23
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export const hyperscript = (tag, attrs, children="") => {
|
|
2
|
+
const HasChildren = !!children;
|
|
3
|
+
|
|
4
|
+
if(tag){
|
|
5
|
+
children = ',""' + children + ',""'
|
|
6
|
+
const splitted = splitQuotedLines(children);
|
|
7
|
+
children = insertBetween(splitted, 'van.tags.br()')
|
|
8
|
+
children[children.length - 1] = children.at(-1).slice(0, -3)
|
|
9
|
+
children[0] = children.at(0).slice(3)
|
|
10
|
+
}
|
|
11
|
+
return `van.tags.${tag}(${attrs}${HasChildren ?`, ${children}` : ""})`
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function splitQuotedLines(str) {
|
|
15
|
+
return str
|
|
16
|
+
.slice(1, -1)
|
|
17
|
+
.split(/\r\n|\r|\n/)
|
|
18
|
+
.map(line => `"${line}"`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function insertBetween(arr, value) {
|
|
22
|
+
const result = [];
|
|
23
|
+
for (let i = 0; i < arr.length; i++) {
|
|
24
|
+
result.push(arr[i]);
|
|
25
|
+
if (i < arr.length - 1) {
|
|
26
|
+
result.push(value);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { parse } from 'yaml';
|
|
2
|
+
|
|
3
|
+
const parseYml = yml => {
|
|
4
|
+
const {__props__, ...__attrs__} = yml ? parse(yml) : {__props__ : {}} ;
|
|
5
|
+
const HasAttributs = Object.keys(__attrs__).length > 0
|
|
6
|
+
return {
|
|
7
|
+
props : __props__,
|
|
8
|
+
attrs : HasAttributs
|
|
9
|
+
?[
|
|
10
|
+
`const {${Object.keys(__attrs__).join(",")}} = ${JSON.stringify(__attrs__,"",2)}`,
|
|
11
|
+
`export {${Object.keys(__attrs__).join(", ")}}`
|
|
12
|
+
].join("\n")
|
|
13
|
+
: "",
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const stringifyProps = (props) =>{
|
|
18
|
+
return props
|
|
19
|
+
?`{${Object.entries(props).map(([key, value]) => `${key} = ${JSON.stringify(value)}`).join(", ")}}={}`
|
|
20
|
+
:""
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export {parseYml, stringifyProps}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const processAttribute = (attributes) =>{
|
|
2
|
+
if(attributes.length === 0) return "{}"
|
|
3
|
+
let attr = []
|
|
4
|
+
for(let i=0; i<attributes.length ; i++){
|
|
5
|
+
let {name, value} = attributes[i]
|
|
6
|
+
attr.push({
|
|
7
|
+
name,
|
|
8
|
+
value : typeof value === "string" ? value : value.value,
|
|
9
|
+
isExpression : typeof value !== "string"
|
|
10
|
+
})
|
|
11
|
+
}
|
|
12
|
+
return `{${attr.map(({name, value, isExpression})=>`${name}:${isExpression ? value : `"${value}"`}`).join(", ")}}`
|
|
13
|
+
}
|