-
Notifications
You must be signed in to change notification settings - Fork 5
/
converter-lib.html
258 lines (246 loc) · 22.2 KB
/
converter-lib.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
<!DOCTYPE html>
<html lang="en"><head>
<link rel="icon" href="/logo.svg">
<title>VanJS - MD and HTML to VanJS Code Converter</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/poppins/v20/pxiEyp8kv8JHgFVrJJfedw.ttf) format('truetype');
}
</style>
<link rel="stylesheet" href="/code/w3-v1.css">
<link rel="stylesheet" href="/code/prism-v1.css">
<link rel="stylesheet" href="/vanjs.css">
</head>
<body class="line-numbers" data-prismjs-copy="📋">
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Q0NB75RY7E"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-Q0NB75RY7E');
</script>
<script type="text/javascript" src="/code/prism-v1.js" defer></script>
<!-- Sidebar/menu -->
<nav class="w3-sidebar w3-red w3-collapse w3-top w3-large w3-padding" style="z-index:3;width:280px;font-weight:bold;" id="mySidebar"><br>
<a href="javascript:void(0)" onclick="w3_close()" class="w3-button w3-hide-large w3-display-topleft" style="width:100%;font-size:22px">Close Menu</a>
<div class="w3-container">
<h1 class="w3-padding-16 w3-xxxlarge">
<img src="/logo.svg" alt="VanJS" width="192px" height="192px">
</h1>
</div>
<div id="nav" class="w3-bar-block"><a href="/" onclick="w3_close()" class="w3-bar-item w3-button w3-hover-white">Home</a><a href="/start" onclick="w3_close()" class="w3-bar-item w3-button w3-hover-white">Getting Started</a><a href="/tutorial" onclick="w3_close()" class="w3-bar-item w3-button w3-hover-white">Tutorial</a><a href="/demo" onclick="w3_close()" class="w3-bar-item w3-button w3-hover-white">VanJS by Example</a><a href="/convert" onclick="w3_close()" class="w3-bar-item w3-button w3-hover-white">HTML/MD to VanJS</a><a href="/vanui" onclick="w3_close()" class="w3-bar-item w3-button w3-hover-white">VanUI</a><a href="/minivan" onclick="w3_close()" class="w3-bar-item w3-button w3-hover-white">Mini-Van</a><a href="/ssr" onclick="w3_close()" class="w3-bar-item w3-button w3-hover-white">SSR & Hydration</a><a href="/x" onclick="w3_close()" class="w3-bar-item w3-button w3-hover-white">X</a><a href="/advanced" onclick="w3_close()" class="w3-bar-item w3-button w3-hover-white">Advanced Topics</a><a href="/media" onclick="w3_close()" class="w3-bar-item w3-button w3-hover-white">Media Coverage</a><a href="/about" onclick="w3_close()" class="w3-bar-item w3-button w3-hover-white">About</a></div>
</nav>
<!-- Top menu on small screens -->
<header class="w3-container w3-top w3-hide-large w3-red w3-xlarge w3-padding">
<a href="javascript:void(0)" class="w3-button w3-red w3-margin-right" onclick="w3_open()">☰</a>
<span id="title-bar"></span>
</header>
<!-- Overlay effect when opening sidebar on small screens -->
<div class="w3-overlay w3-hide-large" onclick="w3_close()" style="cursor:pointer" title="close side menu" id="myOverlay"></div>
<!-- !PAGE CONTENT! -->
<div class="w3-main" style="margin-left:300px;">
<div id="page">
<div id="content"><h1 class="w3-xxlarge">HTML and MD to VanJS Code Converter</h1><p>This is a library that can convert any MD or HTML snippet into valid <strong>VanJS</strong> code. The UI version of the code converter is <a href="https://vanjs.org/convert" class="w3-hover-opacity">here</a>.</p><h2 class="w3-xxlarge w3-text-red" id="installation"><a class="self-link" href="#installation">Installation</a></h2><hr style="width:50px;border:5px solid red" class="w3-round"><p>The library is published as NPM package <a href="https://www.npmjs.com/package/vanjs-converter" class="w3-hover-opacity">vanjs-converter</a>.</p><p>Run the following command to install the package:</p><pre><code class="language-shell">npm install vanjs-converter
</code></pre><p>To use the NPM package, add this line to your script:</p><pre><code class="language-js">import { htmlToVanCode, mdToVanCode } from "vanjs-converter"
</code></pre><h2 class="w3-xxlarge w3-text-red" id="htmltovancode-convert-html-snippet-to-vanjs-code"><a class="self-link" href="#htmltovancode-convert-html-snippet-to-vanjs-code"><code class="symbol">htmlToVanCode</code>: Convert HTML snippet to VanJS Code</a></h2><hr style="width:50px;border:5px solid red" class="w3-round"><h3 class="w3-large w3-text-red" id="signature"><a class="self-link" href="#signature">Signature</a></h3><pre><code class="language-js">htmlToVanCode(<HTML string>, <options>) => {code: <code>, tags: <tags>, components: <components>}
</code></pre><h3 class="w3-large w3-text-red" id="example"><a class="self-link" href="#example">Example</a></h3><pre><code class="language-js">htmlToVanCode('<div><p>👋Hello</p><ul><li>🗺️World</li><li><a href="https://vanjs.org/">🍦VanJS</a></li></ul></div>', {indent: 4})
/*
The following result will be returned:
{
code: [
'div(',
' p(',
' "👋Hello",',
' ),',
' ul(',
' li(',
' "🗺️World",',
' ),',
' li(',
' a({href: "https://vanjs.org/"},',
' "🍦VanJS",',
' ),',
' ),',
' ),',
')',
],
tags: ["a", "div", "li", "p", "ul"],
components: [],
}
*/
</code></pre><h3 class="w3-large w3-text-red" id="using-vanjs-components"><a class="self-link" href="#using-vanjs-components">Using VanJS Components</a></h3><p><em>This is only supported in the converter library, not in the UI. The <a href="https://github.com/remarkablemark/html-react-parser/issues/168#issuecomment-699536994" class="w3-hover-opacity">root cause</a> is <a href="https://www.npmjs.com/package/html-dom-parser" class="w3-hover-opacity">html-dom-parser</a> doesn't support case-sensitive parsing on the client side.</em></p><p>The input HTML string can be a mix of HTML elements and custom UI components built with <strong>VanJS</strong>. To use custom UI components, just specify the component similar to regular HTML tags. For instance, assume we have custom UI components similar to the ones shown in <a href="https://vanjs.org/" class="w3-hover-opacity">https://vanjs.org/</a> home page:</p><pre><code class="language-js">const Hello = text => div(
p("👋Hello"),
ul(
li(text),
li(a({href: "https://vanjs.org/"}, "🍦VanJS")),
),
)
const Counter = ({initValue}) => {
const counter = van.state(initValue)
return button({onclick: () => ++counter.val}, counter)
}
</code></pre><p>You can simply specify the input HTML string like this:</p><pre><code class="language-html"><h2>Hello</h2>
<Hello>🗺️World</Hello>
<h2>Counter</h2>
<Counter initValue="1"></Counter>
<Counter initValue="2"></Counter>
</code></pre><p>which will be converted into the following <strong>VanJS</strong> code:</p><pre><code class="language-js">h2(
"Hello",
),
Hello(
"🗺️World",
),
h2(
"Counter",
),
Counter({initValue: "1"}),
Counter({initValue: "2"}),
</code></pre><h3 class="w3-large w3-text-red" id="options"><a class="self-link" href="#options">Options</a></h3><ul><li><p><code class="symbol">indent</code>: Type <code class="symbol">number</code>. Default <code class="symbol">2</code>. Optional. The indent level of the generated <strong>VanJS code</strong>.</p></li><li><p><code class="symbol">spacing</code>: Type <code class="symbol">boolean</code>. Default <code class="symbol">false</code>. Optional. The style of the property object in the generated <strong>VanJS</strong> code. If <code class="symbol">true</code>, the property object will look like <code class="symbol">{href: "https://vanjs.org/"}</code>; Otherwise, the property object will look like <code class="symbol">{ href: "https://vanjs.org/" }</code>.</p></li><li><p><code class="symbol">skipEmptyText</code>: Type <code class="symbol">boolean</code>. Default <code class="symbol">false</code>. Optional. Whether to skip empty text nodes in the generated <strong>VanJS code</strong>. For instance, the HTML snippet:</p><pre><code class="language-html"><div>
<p>👋Hello</p>
<ul>
<li>🗺️World</li>
<li><a href="https://vanjs.org/">🍦VanJS</a></li>
</ul>
</div>
</code></pre><p>will be converted to:</p><pre><code class="language-js">div(
p(
"👋Hello",
),
ul(
li(
"🗺️World",
),
li(
a({href: "https://vanjs.org/"},
"🍦VanJS",
),
),
),
)
</code></pre><p>if <code class="symbol">skipEmptyText</code> is <code class="symbol">true</code>. But it will be converted to:</p><pre><code class="language-js">div(
"\n ",
p(
"👋Hello",
),
"\n ",
ul(
"\n ",
li(
"🗺️World",
),
"\n ",
li(
a({href: "https://vanjs.org/"},
"🍦VanJS",
),
),
"\n ",
),
"\n",
)
</code></pre><p>if <code class="symbol">skipEmptyText</code> is <code class="symbol">false</code>.</p></li><li><p><code class="symbol">htmlTagPred</code>: Type <code class="symbol">(name: string) => boolean</code>. Default <code class="symbol">s => s.toLowerCase() === s</code>. Optional. A predicate function to check whether a specific tag snippet such as <code class="symbol"><Counter></code> should be treated as a native HTML element or a custom UI component built with <strong>VanJS</strong>. By default, it will be treated as a native HTML element if the letters in the <code class="symbol">name</code> are all lowercase.</p></li></ul><h3 class="w3-large w3-text-red" id="return-value"><a class="self-link" href="#return-value">Return Value</a></h3><p>A plain object with the following fields:</p><ul><li><code class="symbol">code</code>: A <code class="symbol">string[]</code> for all lines of the generated <strong>VanJS</strong> code.</li><li><code class="symbol">tags</code>: A <code class="symbol">string[]</code> for all HTML tag names used in the generated <strong>VanJS</strong> code, which can be used in the importing line of tag functions such as:<pre><code class="language-js">const {<tags needs to import>} = van.tags
</code></pre></li><li><code class="symbol">components</code>: A <code class="symbol">string[]</code> for all custom <strong>VanJS</strong> components used in the generated <strong>VanJS</strong> code, which can be used in the importing line such as:<pre><code class="language-js">import {<components needs to import>} from "./my-component-lib.js"
</code></pre></li></ul><h3 class="w3-large w3-text-red" id="dummy"><a class="self-link" href="#dummy"><code class="symbol">DUMMY</code></a></h3><p><em>This is only supported in the converter library, not in the UI.</em></p><p>There are 2 special cases while specifying custom <strong>VanJS</strong> components in the input HTML string. The first special case is that, sometimes, a custom component needs properties being specified in its first argument, even for empty properties <code class="symbol">{}</code> (e.g.: the <code class="symbol">Counter</code> component defined in the <a href="#using-vanjs-components" class="w3-hover-opacity">section</a> above). In this case, you can specify the special <code class="symbol">DUMMY</code> property as a placeholder. For instance:</p><pre><code class="language-html"><CustomElement DUMMY>content</CustomElement>
</code></pre><p>will be converted to:</p><pre><code class="language-js">CustomElement({},
"content",
)
</code></pre><p>whereas</p><pre><code class="language-html"><CustomElement>content</CustomElement>
</code></pre><p>will be converted to:</p><pre><code class="language-js">CustomElement(
"content",
)
</code></pre><p>The second special case is that, sometimes, a custom <strong>VanJS</strong> component needs consecutive string arguments. You can achieve that by inserting <code class="symbol"><DUMMY></code> element between text pieces. For instance:</p><pre><code class="language-html"><Link>🍦VanJS<DUMMY></DUMMY>https://vanjs.org/</Link>
</code></pre><p>will be converted to:</p><pre><code class="language-js">Link(
"🍦VanJS",
"https://vanjs.org/",
)
</code></pre><h2 class="w3-xxlarge w3-text-red" id="mdtovancode-convert-md-snippet-to-vanjs-code"><a class="self-link" href="#mdtovancode-convert-md-snippet-to-vanjs-code"><code class="symbol">mdToVanCode</code>: Convert MD snippet to VanJS Code</a></h2><hr style="width:50px;border:5px solid red" class="w3-round"><h3 class="w3-large w3-text-red" id="signature-1"><a class="self-link" href="#signature-1">Signature</a></h3><pre><code class="language-js">mdToVanCode(<MD string>, <options>) => {code: <code>, tags: <tags>, components: <components>}
</code></pre><p>Under the hood, there are 2 steps for converting an MD snippet to <strong>VanJS</strong> code:</p><ol><li>Convert the MD string into an HTML string with <a href="https://marked.js.org/" class="w3-hover-opacity">Marked</a> library.</li><li>Convert the HTML string into <strong>VanJS</strong> code with <code class="symbol">htmlToVanCode</code>.</li></ol><h3 class="w3-large w3-text-red" id="example-1"><a class="self-link" href="#example-1">Example</a></h3><pre><code class="language-js">mdToVanCode(`👋Hello
* 🗺️World
* [🍦VanJS](https://vanjs.org/)
`)
/*
The following result will be returned:
{
code: [
'p(',
' "👋Hello",',
'),',
'ul(',
' li(',
' "🗺️World",',
' ),',
' li(',
' a({href: "https://vanjs.org/"},',
' "🍦VanJS",',
' ),',
' ),',
'),',
],
tags: ["a", "li", "p", "ul"],
components: [],
}
*/
</code></pre><p>Note that, you can insert custom HTML snippets, or even <a href="#using-vanjs-components" class="w3-hover-opacity">custom <strong>VanJS</strong> components</a> in the input MD string.</p><h3 class="w3-large w3-text-red" id="options-1"><a class="self-link" href="#options-1">Options</a></h3><ul><li><p><code class="symbol">indent</code>: Type <code class="symbol">number</code>. Default <code class="symbol">2</code>. Optional. The indent level of the generated <strong>VanJS code</strong>.</p></li><li><p><code class="symbol">spacing</code>: Type <code class="symbol">boolean</code>. Default <code class="symbol">false</code>. Optional. The style of the property object in the generated <strong>VanJS</strong> code. If <code class="symbol">true</code>, the property object will look like <code class="symbol">{href: "https://vanjs.org/"}</code>; Otherwise, the property object will look like <code class="symbol">{ href: "https://vanjs.org/" }</code>.</p></li><li><p><code class="symbol">htmlTagPred</code>: Type <code class="symbol">(name: string) => boolean</code>. Default <code class="symbol">s => s.toLowerCase() === s</code>. Optional. A predicate function to check whether a specific tag snippet such as <code class="symbol"><Counter></code> represents a native HTML element or a custom UI component built with <strong>VanJS</strong>. By default, it will be considered a native HTML element if the letters in the <code class="symbol">name</code> are all lowercase.</p></li><li><p><code class="symbol">renderer</code>: Optional. <em>Custom renderer is only supported in the converter library, not in the UI.</em> A custom object used to override how tokens in the MD string are being rendered. The specification of the <code class="symbol">renderer</code> object can be found in Marked <a href="https://marked.js.org/using_pro#renderer" class="w3-hover-opacity">doc</a>. For instance, the <code class="symbol">renderer</code> object:</p><pre><code class="language-js">{
codespan: s => `<Symbol>${s}</Symbol>`,
link: (href, _unused_title, text) => `<Link>${text}<DUMMY></DUMMY>${href}</Link>`,
}
</code></pre><p>will convert <code class="symbol">`text`</code> in MD string into <code class="symbol">Symbol("text")</code> (here <code class="symbol">Symbol</code> is a custom <strong>VanJS</strong> component) instead of <code class="symbol">code("text")</code>, and will convert <code class="symbol">[text](link)</code> in MD string into <code class="symbol">Link("text", "link")</code> instead of <code class="symbol">a({href: "link"}, "text")</code>.</p></li></ul><h3 class="w3-large w3-text-red" id="return-value-1"><a class="self-link" href="#return-value-1">Return Value</a></h3><p>The same as the <a href="#return-value" class="w3-hover-opacity">return value</a> of <code class="symbol">htmlToVanCode</code>.</p><h2 class="w3-xxlarge w3-text-red" id="showroom"><a class="self-link" href="#showroom">Showroom</a></h2><hr style="width:50px;border:5px solid red" class="w3-round"><p>The <a href="https://vanjs.org/" class="w3-hover-opacity">https://vanjs.org/</a> website is using this library to keep <code class="symbol">README.md</code> files in sync with their corresponding web pages (<a href="https://github.com/vanjs-org/vanjs-org.github.io/tree/master/codegen" class="w3-hover-opacity">source code</a> of the code generation):</p><ul><li>The <a href="https://vanjs.org/vanui" class="w3-hover-opacity">VanUI</a> page is kept in sync with the <a href="https://github.com/vanjs-org/van/tree/main/components#readme" class="w3-hover-opacity"><code class="symbol">README.md</code></a> file in GitHub with the help of this library.</li><li>This <a href="https://github.com/vanjs-org/converter#readme" class="w3-hover-opacity"><code class="symbol">README.md</code></a> file is kept in sync with this <a href="https://vanjs.org/converter-lib" class="w3-hover-opacity">page</a> in <a href="https://vanjs.org/" class="w3-hover-opacity">https://vanjs.org/</a> website.</li></ul></div>
<aside id="toc"><ul><li><a href="#installation" class="w3-hover-opacity">Installation</a></li><li><a href="#htmltovancode-convert-html-snippet-to-vanjs-code" class="w3-hover-opacity">htmlToVanCode: Convert HTML snippet to VanJS Code</a><ul><li><a href="#signature" class="w3-hover-opacity">Signature</a></li><li><a href="#example" class="w3-hover-opacity">Example</a></li><li><a href="#using-vanjs-components" class="w3-hover-opacity">Using VanJS Components</a></li><li><a href="#options" class="w3-hover-opacity">Options</a></li><li><a href="#return-value" class="w3-hover-opacity">Return Value</a></li><li><a href="#dummy" class="w3-hover-opacity">DUMMY</a></li></ul></li><li><a href="#mdtovancode-convert-md-snippet-to-vanjs-code" class="w3-hover-opacity">mdToVanCode: Convert MD snippet to VanJS Code</a><ul><li><a href="#signature-1" class="w3-hover-opacity">Signature</a></li><li><a href="#example-1" class="w3-hover-opacity">Example</a></li><li><a href="#options-1" class="w3-hover-opacity">Options</a></li><li><a href="#return-value-1" class="w3-hover-opacity">Return Value</a></li></ul></li><li><a href="#showroom" class="w3-hover-opacity">Showroom</a></li></ul></aside>
</div>
</div>
<script>
// Script to open and close sidebar
const w3_open = () => {
document.getElementById("mySidebar").style.display = "block"
document.getElementById("myOverlay").style.display = "block"
}
const w3_close = () => {
document.getElementById("mySidebar").style.display = "none"
document.getElementById("myOverlay").style.display = "none"
}
const tocDom = document.getElementById("toc")
// Tracks the current toc item
const trackToc = () => {
const allHeadings = [...document.querySelectorAll("h2,h3")]
const currentHeadingIndex = allHeadings.findIndex(h => h.getBoundingClientRect().top >= 0)
let currentHeading
if (currentHeadingIndex < 0) currentHeading = allHeadings[allHeadings.length - 1]; else {
currentHeading = allHeadings[currentHeadingIndex]
if (currentHeadingIndex > 0 && currentHeading.getBoundingClientRect().top > innerHeight)
currentHeading = allHeadings[currentHeadingIndex - 1]
}
for (const e of document.querySelectorAll("#toc li a"))
if (e.href.split("#")[1] === currentHeading?.id) {
e.classList.add("current-heading")
const {top: tocTop, bottom: tocBottom} = tocDom.getBoundingClientRect()
const {top: eTop, bottom: eBottom} = e.getBoundingClientRect()
if (eBottom > tocBottom) tocDom.scrollTop += eBottom - tocBottom
else if (eTop < tocTop) tocDom.scrollTop -= tocTop - eTop
} else
e.classList.remove("current-heading")
}
trackToc()
document.addEventListener("scroll", trackToc)
addEventListener("resize", trackToc)
const copy = e => {
const file = e.previousElementSibling.innerText
const importLine = file.includes("nomodule") ?
`<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/vanjs-org/van/public/${file}"><\/script>` :
`import van from "https://cdn.jsdelivr.net/gh/vanjs-org/van/public/${file}"`
navigator.clipboard.writeText(importLine)
.then(() => e.querySelector(".tooltip").innerText = "Copied")
.catch(() => e.querySelector(".tooltip").innerText = "Copy failed")
}
const resetTooltip = e => e.querySelector(".tooltip").innerText = "Copy import line"
</script>
<script src="https://guru-widget.pages.dev/guru_widget.latest.min.js" data-text="Ask AI" data-link="https://gurubase.io/g/vanjs" data-bg-color="rgba(244, 67, 54, 0.3)" data-icon-url="/ask_ai.svg" data-font-color="#ffffff" data-margins="{"bottom": "1rem", "left": "1430px", "right": "unset"}">
</script>
<!-- Place this tag in your head or just before your close body tag. -->
<script async defer src="https://buttons.github.io/buttons.js"></script>
<link rel="prefetch" href="/code/prism-v1.js" as="script"><link rel="prefetch" href="https://www.gstatic.com/charts/loader.js" as="script"><link rel="prefetch" href="/code/diff.min.js" as="script"><link rel="prefetch" href="/code/van-1.5.2.nomodule.min.js" as="script"><link rel="prefetch" href="/code/van-x-0.6.2.nomodule.min.js" as="script"></body></html>