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 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,2 @@
1
+ export * from "./process.js";
2
+ export * from "./transpile.js";
@@ -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,5 @@
1
+ export const componentType = tag => {
2
+ if(tag === "script") return "script";
3
+ if(tag.toLowerCase() !== tag || tag.includes(".")) return "jsx";
4
+ return "html"
5
+ }
@@ -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,4 @@
1
+ export { componentType } from "./component-type.js";
2
+ export { processAttribute } from "./process-attributes.js";
3
+ export { parseYml } from "./parse-yml.js";
4
+ export { hyperscript } from "./hyperscript.js"
@@ -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
+ }