<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Mirone</title>
    <description>The latest articles on Forem by Mirone (@saulmirone).</description>
    <link>https://forem.com/saulmirone</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F373050%2F2de9a8e2-2fc9-4216-9e3d-87dc7454db5b.jpg</url>
      <title>Forem: Mirone</title>
      <link>https://forem.com/saulmirone</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/saulmirone"/>
    <language>en</language>
    <item>
      <title>Markdown editor for freedom!</title>
      <dc:creator>Mirone</dc:creator>
      <pubDate>Wed, 22 Dec 2021 05:53:36 +0000</pubDate>
      <link>https://forem.com/saulmirone/markdown-editor-for-freedom-3jo1</link>
      <guid>https://forem.com/saulmirone/markdown-editor-for-freedom-3jo1</guid>
      <description>&lt;h2&gt;
  
  
  Long Live Markdown
&lt;/h2&gt;

&lt;p&gt;The content editor is a very important function for the authoring platform. A powerful editor allows creators to focus on creation. And one of the best ways to please programmer creators is to support Markdown writing, because most programmers use Markdown to write articles.&lt;/p&gt;

&lt;p&gt;Markdown, as a programmer’s love for writing, has many advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Typesetting is achieved through grammar, no need to click to manually set the style&lt;/li&gt;
&lt;li&gt;Quickly implement complex content, such as: code blocks, hyperlinks, formulas, etc.&lt;/li&gt;
&lt;li&gt;Give creators more time to focus on content&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, the same has some disadvantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There is a certain learning threshold, not very friendly to non-programmers&lt;/li&gt;
&lt;li&gt;Looking at the original document is like looking at the "code", the preview effect requires tool or editor support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Is there a way to keep the convenience brought by Markdown while lowering the barriers to entry? Most old players will blurt out: Typora.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FHelloGitHub-Team%2FArticle%2Fblob%2Fmaster%2Fcontents%2FJavaScript%2Fmilkdown%2Fimg%2F2.gif%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FHelloGitHub-Team%2FArticle%2Fblob%2Fmaster%2Fcontents%2FJavaScript%2Fmilkdown%2Fimg%2F2.gif%3Fraw%3Dtrue" alt="typora"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Typora is great, however, it isn't free anymore. And because of that it is not open source, if you want to implement a similar Markdown editor in your own project, you need to find another solution.&lt;/p&gt;
&lt;h2&gt;
  
  
  Introducing Milkdown
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://milkdown.dev/" rel="noopener noreferrer"&gt;Milkdown&lt;/a&gt; is a WYSIWYG (what you see is what you get) editor.&lt;br&gt;
It's an open source project that integrates Markdown editor, components, and plugins.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Github repo: &lt;a href="https://github.com/Saul-Mirone/milkdown" rel="noopener noreferrer"&gt;https://github.com/Saul-Mirone/milkdown&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.meo.cool%2F_next%2Fimage%3Furl%3D%252FMilkdown%252F3-Principle_of_MIlkdown.png%26w%3D1920%26q%3D75" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.meo.cool%2F_next%2Fimage%3Furl%3D%252FMilkdown%252F3-Principle_of_MIlkdown.png%26w%3D1920%26q%3D75" alt="principle"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It has all the functions you want, and the functions you don't need can also be reduced by deleting plugins. The design idea of the plugin allows you to customize the Markdown editor most suitable for you in several minutes!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  📝 &lt;strong&gt;WYSIWYG Markdown&lt;/strong&gt; - Write markdown in an elegant way&lt;/li&gt;
&lt;li&gt;  🎨 &lt;strong&gt;Themable&lt;/strong&gt; - Theme can be shared and used with npm packages&lt;/li&gt;
&lt;li&gt;  🎮 &lt;strong&gt;Hackable&lt;/strong&gt; - Support your awesome idea by plugin&lt;/li&gt;
&lt;li&gt;  🦾 &lt;strong&gt;Reliable&lt;/strong&gt; - Built on top of &lt;a href="https://prosemirror.net/" rel="noopener noreferrer"&gt;prosemirror&lt;/a&gt; and &lt;a href="https://github.com/remarkjs/remark" rel="noopener noreferrer"&gt;remark&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  ⚡ &lt;strong&gt;Slash &amp;amp; Tooltip&lt;/strong&gt; - Write fast for everyone&lt;/li&gt;
&lt;li&gt;  🧮 &lt;strong&gt;Math&lt;/strong&gt; - LaTeX math equations support&lt;/li&gt;
&lt;li&gt;  📊 &lt;strong&gt;Table&lt;/strong&gt; - Table support with fluent ui&lt;/li&gt;
&lt;li&gt;  📰 &lt;strong&gt;Diagram&lt;/strong&gt; - Diagram support with &lt;a href="https://mermaid-js.github.io/mermaid/#/" rel="noopener noreferrer"&gt;mermaid&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  🍻 &lt;strong&gt;Collaborate&lt;/strong&gt; - Shared editing support with &lt;a href="https://docs.yjs.dev/" rel="noopener noreferrer"&gt;yjs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  💾 &lt;strong&gt;Clipboard&lt;/strong&gt; - Support copy and paste markdown&lt;/li&gt;
&lt;li&gt;  👍 &lt;strong&gt;Emoji&lt;/strong&gt; - Support emoji shortcut and picker&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Get Started
&lt;/h2&gt;

&lt;p&gt;We provide two ways for you to try it out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://milkdown.dev/#/online-demo" rel="noopener noreferrer"&gt;Online editor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=mirone.milkdown" rel="noopener noreferrer"&gt;VSCode extension&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Show Case
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Table Editing
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FHelloGitHub-Team%2FArticle%2Fraw%2Fmaster%2Fcontents%2FJavaScript%2Fmilkdown%2Fimg%2F6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FHelloGitHub-Team%2FArticle%2Fraw%2Fmaster%2Fcontents%2FJavaScript%2Fmilkdown%2Fimg%2F6.gif" alt="table editing"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Copy and Paste as Markdown
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcuyhvt96v9qm394jgsam.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcuyhvt96v9qm394jgsam.gif" alt="copy and past as Markdown"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Collaborative Editing
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9khxjzv2655zs3wlznwn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9khxjzv2655zs3wlznwn.gif" alt="collaborative editing"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Two Way Binding
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwaqe04cx4oqh3c51jopc.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwaqe04cx4oqh3c51jopc.gif" alt="two way binding"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Drag and Drop
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffay00r4i2r0gfzyjkn50.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffay00r4i2r0gfzyjkn50.gif" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Build You Own Editor
&lt;/h2&gt;

&lt;p&gt;First you need to create a new project, here I recommend you to use &lt;a href="https://vitejs.dev/" rel="noopener noreferrer"&gt;vite&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init vite@latest my-milkdown-app &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--template&lt;/span&gt; vanilla
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The core of Milkdown and various plugins are independent NPM packages, which can be installed directly through NPM.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @milkdown/core @milkdown/preset-commonmark @milkdown/theme-nord
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you can create your own milkdown editor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Editor&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@milkdown/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;nord&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@milkdown/theme-nord&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;commonmark&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@milkdown/preset-commonmark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;Editor&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nord&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;commonmark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/milkdown-vanilla-setup-8xobc"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Plugin is the first class member of Milkdown, it is essentially a plugin loader, and all functions are provided through plugins. A table is a plugin, a theme is a plugin, and even a simple line of text is also a plugin.&lt;/p&gt;

&lt;p&gt;At present, the official has provided many plug-ins to ensure that they can be used out of the box. Only some plugins are listed below:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;th&gt;description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/@milkdown/preset-commonmark" rel="noopener noreferrer"&gt;@milkdown/preset-commonmark&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Add &lt;a href="https://commonmark.org/" rel="noopener noreferrer"&gt;commonmark&lt;/a&gt; syntax support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/@milkdown/preset-gfm" rel="noopener noreferrer"&gt;@milkdown/preset-gfm&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Add &lt;a href="https://github.github.com/gfm/" rel="noopener noreferrer"&gt;gfm&lt;/a&gt; syntax support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/@milkdown/plugin-history" rel="noopener noreferrer"&gt;@milkdown/plugin-history&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Add undo &amp;amp; redo support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/@milkdown/plugin-clipboard" rel="noopener noreferrer"&gt;@milkdown/plugin-clipboard&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Add markdown copy &amp;amp; paste support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/@milkdown/plugin-cursor" rel="noopener noreferrer"&gt;@milkdown/plugin-cursor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Add drop &amp;amp; gap cursor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/@milkdown/plugin-listener" rel="noopener noreferrer"&gt;@milkdown/plugin-listener&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Add listener support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/@milkdown/plugin-collaborative" rel="noopener noreferrer"&gt;@milkdown/plugin-collaborative&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Add collaborative editing support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/@milkdown/plugin-table" rel="noopener noreferrer"&gt;@milkdown/plugin-table&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Add table syntax support (already included in gfm)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/@milkdown/plugin-prism" rel="noopener noreferrer"&gt;@milkdown/plugin-prism&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Add &lt;a href="https://prismjs.com/" rel="noopener noreferrer"&gt;prism&lt;/a&gt; support for code block highlight&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/@milkdown/plugin-math" rel="noopener noreferrer"&gt;@milkdown/plugin-math&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Add &lt;a href="https://en.wikipedia.org/wiki/LaTeX" rel="noopener noreferrer"&gt;LaTeX&lt;/a&gt; support for math&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/@milkdown/plugin-tooltip" rel="noopener noreferrer"&gt;@milkdown/plugin-tooltip&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Add selected tooltip for text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/@milkdown/plugin-slash" rel="noopener noreferrer"&gt;@milkdown/plugin-slash&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Add slash commands support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/@milkdown/plugin-emoji" rel="noopener noreferrer"&gt;@milkdown/plugin-emoji&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Add emoji support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/@milkdown/plugin-diagram" rel="noopener noreferrer"&gt;@milkdown/plugin-diagram&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Add &lt;a href="https://mermaid-js.github.io/mermaid/#/" rel="noopener noreferrer"&gt;mermaid&lt;/a&gt; diagram support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/@milkdown/plugin-indent" rel="noopener noreferrer"&gt;@milkdown/plugin-indent&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Add tab indent support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/@milkdown/plugin-upload" rel="noopener noreferrer"&gt;@milkdown/plugin-upload&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Add drop and upload support&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You can also create your own plugin following this guide:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://milkdown.dev/#/writing-syntax-plugins" rel="noopener noreferrer"&gt;Writing Syntax Plugins&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Before starting this project, I tried various Markdown editors, but I didn't find one that was particularly satisfactory. Because they are all closed source, and the functions are provided by the developers, some functions are too bloated, and some are too simple. In this case, I simply made a Markdown editor that can be easily customized and can be easily used by non-programmers, and I have the Milkdown that everyone sees.&lt;/p&gt;

&lt;p&gt;I hope that the open source Milkdown will give users more freedom of choice and break the "monopoly" of the Markdown editor. Open source is not easy. If Milkdown is helpful to you, please give it a star✨.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Saul-Mirone/milkdown" rel="noopener noreferrer"&gt;https://github.com/Saul-Mirone/milkdown&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>markdown</category>
      <category>writing</category>
    </item>
    <item>
      <title>How to design a Type Friendly Context</title>
      <dc:creator>Mirone</dc:creator>
      <pubDate>Tue, 03 Aug 2021 16:41:15 +0000</pubDate>
      <link>https://forem.com/saulmirone/how-to-design-a-type-friendly-context-i9d</link>
      <guid>https://forem.com/saulmirone/how-to-design-a-type-friendly-context-i9d</guid>
      <description>&lt;p&gt;&lt;a href="https://saul-mirone.github.io/how-to-design-a-type-friendly-context/"&gt;Read this blog on my website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the world of JavaScript, &lt;a href="https://koajs.com/"&gt;Koa&lt;/a&gt; is a milestone. Although &lt;a href="http://sinatrarb.com/"&gt;sinatra&lt;/a&gt; is born before it, Koa makes it really popular that apps should built by a simple core to load plugins, and bundles of plugins to implement unique features.&lt;br&gt;
Today lots of apps are built with this pattern.&lt;br&gt;
For example, &lt;a href="https://code.visualstudio.com/"&gt;vscode&lt;/a&gt; and &lt;a href="https://webpack.js.org/"&gt;webpack&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Context in JavaScript
&lt;/h2&gt;

&lt;p&gt;In the world of Koa, &lt;code&gt;ctx&lt;/code&gt; is a magic box 🔮. Users can get all sorts of properties on it. For example, you can get &lt;code&gt;ctx.session&lt;/code&gt; if you install the &lt;strong&gt;koa-session&lt;/strong&gt; plugin. And you can get &lt;code&gt;ctx.request.body&lt;/code&gt; if you install the &lt;strong&gt;koa-body&lt;/strong&gt; plugin.&lt;/p&gt;

&lt;p&gt;A typical Koa plugin (also known as middleware) will be like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// inject props into ctx&lt;/span&gt;
    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;startTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// do something after other ctx done.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;endTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;endTime&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ctx duration:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Static Type Checking
&lt;/h2&gt;

&lt;p&gt;Everything seems perfect until static type system join the game,&lt;br&gt;
which is bring in by &lt;a href="https://www.typescriptlang.org/"&gt;TypeScript&lt;/a&gt; and &lt;a href="https://flow.org/"&gt;Flow&lt;/a&gt;. With the safe type checking and powerful editor &lt;a href="https://microsoft.github.io/language-server-protocol/"&gt;lsp&lt;/a&gt; features, people use them to build not only large systems, but also small apps and tools.&lt;/p&gt;

&lt;p&gt;But when Koa meets static type checking, 💥 everything stop working. Type system cannot infer what property is really on &lt;code&gt;ctx&lt;/code&gt; and what's not. For example, if I call &lt;code&gt;ctx.foo&lt;/code&gt;, how do I know wether the plugin inject the &lt;code&gt;foo&lt;/code&gt; property is loaded in current Koa app or not? What's more, users can't get the hint of the editor because the type system don't know what to suggest.&lt;/p&gt;

&lt;p&gt;It's a common problem of languages with static type system:&lt;br&gt;
how to handle object shared between modules elegantly?&lt;/p&gt;


&lt;h2&gt;
  
  
  Design
&lt;/h2&gt;

&lt;p&gt;The key is using &lt;a href="https://en.wikipedia.org/wiki/Inversion_of_control"&gt;IoC&lt;/a&gt;. With this pattern we can &lt;em&gt;inject type information into context&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Let's reconsider the design of context in koa, we can see that the context is an object with properties you can modify, such as &lt;code&gt;ctx.foo&lt;/code&gt;. What if we transform this API into &lt;code&gt;ctx.get(foo)&lt;/code&gt;? Since the creation of foo is what we can control, we can write some information on it.&lt;/p&gt;

&lt;p&gt;So, let's assume the API of context is designed as this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createCtx&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;numberSlice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createSlice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// inject a ctx.&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;numberSlice&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; 0&lt;/span&gt;

&lt;span class="c1"&gt;// set value of numberSlice to 1.&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;numberSlice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I introduced you a new data structure: &lt;code&gt;slice&lt;/code&gt;.&lt;br&gt;
With this design, We just &lt;em&gt;split up&lt;/em&gt; the entire &lt;code&gt;ctx&lt;/code&gt; into several pieces of &lt;code&gt;slice&lt;/code&gt;s.&lt;/p&gt;

&lt;p&gt;Now we can get define the structure of &lt;code&gt;ctx&lt;/code&gt; and &lt;code&gt;slice&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Slice&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Slice&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;get&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Slice
&lt;/h2&gt;

&lt;p&gt;Then, let's try to implement the slice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Ctx&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Slice&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createSlice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Slice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;inner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Slice&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;inner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;inner&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;slice&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Slice&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We create a &lt;code&gt;metadata&lt;/code&gt; that brings slice's information on it. And a slice factory that can be used to inject on context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ctx
&lt;/h2&gt;

&lt;p&gt;The implementation of ctx will be much simpler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createCtx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getSlice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Slice&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Slice not injected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Slice&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;getSlice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;getSlice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use a simple &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map"&gt;Map&lt;/a&gt; as the container of slices, with the &lt;code&gt;symbol&lt;/code&gt; as key so the slices will not be conflict between each other.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test
&lt;/h2&gt;

&lt;p&gt;Now our context has been done, let's do some test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createSlice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createCtx&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createCtx&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;ctx1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;ctx2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctx1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// editor will know x is number&lt;/span&gt;
&lt;span class="nx"&gt;ctx1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// this line will have an error since num slice only accept number&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nx"&gt;ctx1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// =&amp;gt; 1&lt;/span&gt;
&lt;span class="nx"&gt;ctx2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// =&amp;gt; still 0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have built a type friendly context using IoC, with slices that can be shared between context, but values will be isolated.&lt;/p&gt;




&lt;p&gt;View full code on &lt;a href="https://codesandbox.io/s/infallible-haibt-ftr93?file=/src/index.ts:1296-1374"&gt;code sandbox&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/context-and-slice-ftr93?module=/src/index.ts"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>programming</category>
    </item>
    <item>
      <title>Introducing Milkdown: A plugin driven WYSIWYG markdown editor</title>
      <dc:creator>Mirone</dc:creator>
      <pubDate>Thu, 08 Jul 2021 18:46:29 +0000</pubDate>
      <link>https://forem.com/saulmirone/introducing-milkdown-a-plugin-driven-wysiwyg-markdown-editor-17n7</link>
      <guid>https://forem.com/saulmirone/introducing-milkdown-a-plugin-driven-wysiwyg-markdown-editor-17n7</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Milkdown is a lightweight but powerful WYSIWYG markdown editor. It's made up by two parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  A tiny core which provide markdown parser, serializer and kinds of plugin loader.&lt;/li&gt;
&lt;li&gt;  Lots of plugins provide syntax, commands and components.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this pattern you can enable or disable any custom syntax you like, such as table, latex and slash commands.&lt;br&gt;
You can even create your own plugin to support your awesome idea.&lt;/p&gt;


&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://saul-mirone.github.io/milkdown/#/online-demo"&gt;online editor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://saul-mirone.github.io/milkdown/"&gt;documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Saul-Mirone/milkdown"&gt;github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  📝 &lt;strong&gt;WYSIWYG Markdown&lt;/strong&gt; - Write markdown in an elegant way&lt;/li&gt;
&lt;li&gt;  🎨 &lt;strong&gt;Themable&lt;/strong&gt; - Theme can be shared and used with npm packages&lt;/li&gt;
&lt;li&gt;  🎮 &lt;strong&gt;Hackable&lt;/strong&gt; - Support your awesome idea by plugin&lt;/li&gt;
&lt;li&gt;  🦾 &lt;strong&gt;Reliable&lt;/strong&gt; - Built on top of &lt;a href="https://prosemirror.net/"&gt;prosemirror&lt;/a&gt; and &lt;a href="https://github.com/remarkjs/remark"&gt;remark&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  ⚡️ &lt;strong&gt;Slash &amp;amp; Tooltip&lt;/strong&gt; - Write fast for everyone, driven by plugin&lt;/li&gt;
&lt;li&gt;  🧮 &lt;strong&gt;Math&lt;/strong&gt; - LaTeX math equations support, driven by plugin&lt;/li&gt;
&lt;li&gt;  📊 &lt;strong&gt;Table&lt;/strong&gt; - Table support with fluent ui, driven by plugin&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;p&gt;Milkdown is built on top of these tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://prosemirror.net/"&gt;Prosemirror&lt;/a&gt; and it's community - A toolkit for building rich-text editors on the web&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://github.com/remarkjs/remark"&gt;Remark&lt;/a&gt; and it's community - Markdown parser done right&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://postcss.org/"&gt;Postcss&lt;/a&gt; - Powerful css tool to build theme&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://www.typescriptlang.org/"&gt;TypeScript&lt;/a&gt; - Developed by typescript&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://prismjs.com/"&gt;Prism&lt;/a&gt; - Code snippets support&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://katex.org/"&gt;Katex&lt;/a&gt; - LaTex math rendering&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  First editor
&lt;/h2&gt;

&lt;p&gt;We have some pieces for you to create a very minimal editor:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;We use &lt;a href="https://fonts.google.com/icons"&gt;material icon&lt;/a&gt; and &lt;a href="https://fonts.google.com/specimen/Roboto"&gt;Roboto Font&lt;/a&gt; in our theme&lt;/strong&gt;.&lt;br&gt;
Make sure to include them for having the best experience.&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Editor&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@milkdown/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;commonmark&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@milkdown/preset-commonmark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// import theme&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@milkdown/theme-nord/lib/theme.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Editor&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;commonmark&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;For further information, please visit our &lt;a href="https://saul-mirone.github.io/milkdown/"&gt;website&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>markdown</category>
    </item>
  </channel>
</rss>
