docsifyjs / docsify

🃏 A magical documentation site generator.
https://docsify.js.org
MIT License
27.49k stars 5.67k forks source link

Encapsulating Docsify #2135

Open trusktr opened 1 year ago

trusktr commented 1 year ago

We need allow the ability to fully encapsulate Docsify features and state.

With encapsulation, it will be possible later on to mount any number of Docsify instances anywhere in a web site, as well as become possible to wrap Docsify as a React/Vue/Svelte/etc component that can be used in any framework.

Encapsulation changes:

jhildenbiddle commented 6 months ago

Offering Docsify as a web component would provide encapsulation. Below is a high-level POC of two Docsify web components: <docsify-site> and <docsify-element> (names TBD).

Docsify "Sites"

A single Docsify web component instance presented as a full-page site (similar to what Docsify renders today). The example below shows a Docisfy site configuration using the default theme and multiple plugins.

<!DOCTYPE html>
<html>
  <head>
    ...
  </head>
  <body>
    <docsify-site></docsify-site>

    <script type="module">
      import Docsify from "...";
      import plugin1 from "...";
      import plugin2 from "...";

      const root = new Docsify({
        plugins: [plugin1, plugin2],
      });      
    </script>
  </body>
</html>

Docsify "Elements"

One or more Docsify web component instances rendered as page elements that co-exist with other non-Docsify page elements. I'm purposely avoiding the word "embed" here since Docsify has its own embeddable content. The examples below shows multiple Docsify elements, each using different themes, custom styles, and plugins.

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="path/to/custom-theme1.css" />
    <link rel="stylesheet" href="path/to/custom-theme2.css" />
  </head>
  <body>
    <!-- Theme and style set via config (see below) -->
    <docsify-element id="foo"></docsify-element >

    <!-- Theme and style set via attributes -->
    <docsify-element 
      id="bar" 
      data-theme="custom-theme2" 
      style="--theme-color: blue;">
    </docsify-element >

    <script type="module">
      import Docsify from "...";
      import plugin1 from "...";
      import plugin2 from "...";

      const rootFoo = new Docsify({
        el: '#foo',
        plugins: [plugin1],
        style: {
          'theme-color': 'red',
        },
        theme: 'custom-theme1',
      });

      const rootBar = new Docsify({
        el: '#bar',
        plugins: [plugin2],
      });
    </script>
  </body>
</html>

If we go this route, life will be easier if we standardize on this as the one and only way to create Docsify instances. If we try to support both an IIFE for standard sites (as we do today) and a web component, everything gets more complicated because plugins, themes, testing, etc. have to support both types of Docsify instances.

To me, this all boils down to an early glimpse of what a release after v5 could look like. Would love to hear what others think.

paulhibbitts commented 6 months ago

Thanks @trusktr @jhildenbiddle I love the sounds of this whole approach from a non-dev perspective, and particularly the use of the standard of Web Components and how nicely that builds (pardon the pun) off the non-build nature of Docsify itself. I also think that for a post v5 feature this leverages, and can benefit from, a recent upswing in interest of Web Components in general, especially with the new KickStarter of Web Awesome that has completely blown past it's initial funding goal🚀

Koooooo-7 commented 6 months ago

Hi @jhildenbiddle ,

Based on the solutions that we makes docsify support multi-instances ( as web-component ) in single page. I think the <docsify-site> and <docsify-element> could be the same thing to me if we could resolve the problems below. That's my assumptions, plz correct me If I do lack of any knowledge on it.

  1. Resolve Isolation Data Isolation, Especially in search (localStorage) or other sharing datas.

    Event Isolation, about the events (click ...).

    Resources Isolation (theme, sidebars...).

  2. Routing IMO, when we provide docsify as the component, it should not bind to the URL directly. Hence we may need a routingDispatcher thing to support the transformation and customize.

There need a way to distinguish the docsify instance and dispatch/manage it correctly. In further, all the plugins/extensions need to be aware of and access it all as well.

Consider that refactoring docsify with Container ( or Namespace ...) scope. The Container is the highest isolation level on each docsify instance. Everything for the docsify instances should be isolated in different unique docsify Container (named same to the docisfy instance id).

So, the component <docsify-site> is a single <docsify-element> within a Default Container (id=default) which has the Default routingDispatcher. The multi <docsify-element>s are in the different Containers with Default routingDispatcher. We only need provide a single Default routingDispatcher since there is no different to dispatch things that mapping URL to one or more Containers.

The global HTML page is just a plain rootContainers with plain/common stuff.

trusktr commented 6 months ago

I really like the idea of a custom element more than a non-custom-element component (f.e. React/Solid/Vue/Svelte/etc) because the custom element can work everywhere.

The only thing though is we need SSR/SSG to work with the custom element.

Besides that, we can totally have a single element.

Routing

I really like the idea of multiple routes combined into a single URL. I thought about this idea before, and making a generic multi-route routing system that could allow any component in an app to have its own routes, and it looked similar to what you depicted above in terms of the URL.

For multi-element routing, each element could have an ID that is incremented from 1 (not random ID) so that the order elements are loaded determines which ID from the route is theirs. Optionally (and recommended) a user could give the elements specific names/ids that are used, so that if the order of elements in the DOM changes it won't have impact on the URLs.

backwards compatibility

We also need to support backwards compatibility for a while, marking it as deprecated for some time (6 moths? 1 year?), while the new format exists. This doesn't have to be complicated though. We simply switch to the new format (custom element), and the "legacy script" would simply construct an instance of the new custom element with JavaScript, insert it into the DOM (f.e. el in legacy global $docsify config), and it would map the rest of the legacy $docsify config options to the element instance.

SSR/SSG

We need to determine a setup that allows us to write a custom element including support for SSR/SSG. Lit has a renderToString API that can be plugged into a system like Astro. Solid also has renderToString, and for lume/element I'd need to update the LumeElement base class to simply call Solid's renderToString instead of plain render. I have a bias towards Lume Element over LitElement (after all, I made LumeElement to be my ideal custom element system, already having experience with Lit and others).

Next steps

How about this: we know SSR/SSG is a must for our future (without eliminating Docsify's key feature of being able to render everything client-side without a build as currently), so perhaps we should have a spike on setting up several SSR/SSG setups using custom elements, to get experience with them and determine what we like. This can highly influence which custom element direction we want to take. We could try:

not sure it would be ideal to support a system that encourages also having something else besides custom elements

Regarding this last part, if we do want to allow users to bring any additional component system, maybe something like Astro is the best for that? Anywho, I think we just have to try some methods out and see what we like.

paulhibbitts commented 5 months ago

Thanks very much @Koooooo-7 @trusktr for the additional details and possible approaches for the use of Web Components to provide an encapsulated Docsify.

While I don't have any technical feedback, I would like to highlight that the non-SSR nature of Docsify is arguably the single most beneficial (and unique) feature of the app and perhaps possible future SSR support could be explored as an add-on, fork etc. This is not only because of the additional development and on-going maintenance required of the Docsify core to support SSR + existing dynamic rendering, but then positioning and describing the app vs. countless other SSR solutions (including those with much bigger development/support teams, which are now available for use as well) could become a significant challenge. Alternatively, as SEO is the underlying goal does the current landscape of static site hosting offer any possible SEO-friendly options worth further testing etc? Sorry if this is off-topic re: encapsulated Docsify, this likely should be further discussed in a separate issue.

trusktr commented 5 months ago

the non-SSR nature of Docsify is arguably the single most beneficial (and unique) feature of the app and perhaps possible future SSR support could be explored as an add-on, fork etc

We will keep this functionality first and foremost! By making a custom element, it will be usable without any build, in a plain HTML file. The SSR mode will be more specialized and opt-in.

countless other SSR solutions (including those with much bigger development/support teams, which are now available for use as well) could become a significant challenge

A possible strategy is to rely on one of those teams, for example the people making Astro have been thinking about SSR and SSG from the start. It will be possible to use a Docsify element in Astro to get SSR/SSG.

does the current landscape of static site hosting offer any possible SEO-friendly options

The static site hosts only serve static files. But someone has to generate those files in the first place. So, for example, Astro's SSG mode can generate output HTML files. Those files are then what you would place onto a static host.

Right now you can put an HTML file and all your markdown files on a static host to have a Docsify site, but that will not be automatically provide good SEO (or be AI friendly, there are now AIs that are reading websites) because the main HTML file with Docsify.js will still be fetching markdown ad dynamically rendering it (not all engines and bots run JavaScript, so they only see the initial HTML without the content generated from markdown).

We need SSG (static site generation) so that we can convert all markdown files into static HTML files ahead of time, and then the output HTML files are what we'd want to place onto a static host for it to be fully SEO/AI-friendly.


My next goal is I will experiment with Astro to get intimately familiar with everything it does, and then I'll add an astro plugin for @lume/element so that elements made with it will support SSR and SSG. Then I'll go from there to think about what would work for Docsify.

I'll work on a version of Docsify with custom elements. In the meantime @jhildenbiddle, if you want to do that too, I think it would be great. I think there is more to it that what we've depicted above, and we don't really know what exactly that will be, so I'm thinking that what we can do is just try starting prototypes on different branches. We could play it by ear, make prototypes to talk about, and eventually converge.

As for my goals with Lume, I want to get it SEO/AI-friendly ASAP, so what I'll do is roll full steam ahead on a new branch to get custom elements working (after I first get Astro with Lume Element working with SSR/SSG). Besides SSR/SSG stuff, we still need to think about how we want the elemebt interface to be, so even if you're working on your own concept to see what that could look like, it would be beneficial.

As an example I've been thinking that <docsify-app> would have various slots like sidebar, navbar, etc, to be able to easily fill in the different parts of the default layout, but then there would need to be some way to provide a full layout/template to replace the default one (I'm not sure exactly how this interface of the element would look like).

We might go in different directions, but then we can converge back together, and we can also think about it as we go along.

So, I'll see you all in a while after I get back from Astro land, or see you there (Astro Discord) if you happen to go there too!