PerfectPan

PerfectPan

Exploring Github Blocks for the first time

What are Github Blocks#

Github Blocks is a way provided by Github to extend your CodeBase. It attempts to change the way you view code on the website. There are two basic units of Blocks: File and Folder. When you browse a code file or code folder, you can use different Blocks to decide how to render your code.

As shown in the picture, the area enclosed in the red box is where we switch Blocks. The entire section below is the result of rendering this excalidraw file.

github-blocks

By clicking on the area enclosed in the red box, we can see other Blocks that can render this file type. We can click to switch, and we can also search for Blocks in the search bar. We can search for text to filter, or we can directly paste the URL of someone else's Block code repository. This is actually to be able to search for hidden Blocks, because if a developer wants to expose a Block externally, they must tag it with github-blocks in their repository for Github to find it. However, we can still find this hidden Block by pasting the URL.

github-blocks-picker

Currently, the official has provided many Blocks, such as Markdown Block, JSON Block, JS Sandbox Block, and so on.

How to develop Github Blocks#

When the existing Blocks in the community do not meet your needs, you can develop a Block yourself. The official also provides a good template code and development tutorial, let's briefly talk about it here.

Through the template code and development tutorial, we can understand that a Block project needs to store a blocks.config.json file in the root directory of the project, which describes the information of your project Block, such as title, description, whether it is a File Block or a Folder Block, recognizable suffixes, such as only files with the .js suffix can use my Block, the entry address for Block loading, etc., supporting the development of multiple Blocks. Then you just need to implement a React component in the corresponding file, and finally use the official scaffold to package your code. Github will inject its exposed capabilities and some lifecycle hooks into the React component.

The overall development experience is quite good. After starting with the scaffold command, we can debug our Block on the Github page. By reading the code of the scaffold, we can know that it is based on vite and esbuild to achieve local development and final packaging. The code packaged in the end is in the format of var BlockBundle = function() { ... }, and React-related libraries are excluded.

// https://github.com/githubnext/blocks-dev/blob/main/scripts/build.js
const esbuild = require("esbuild");
const path = require("path");

process.env.BABEL_ENV = 'production';
process.env.NODE_ENV = 'production';

require('./config/env');

const build = async () => {
  const blocksConfigPath = path.resolve(process.cwd(), "blocks.config.json");
  const blocksConfig = require(blocksConfigPath);

  const blockBuildFuncs = blocksConfig.map((block) => {
    return esbuild.build({
      entryPoints: [`./` + block.entry],
      bundle: true,
      outdir: `dist/${block.id}`,
      format: "iife",
      globalName: "BlockBundle",
      minify: true,
      external: ["fs", "path", "assert", "react", "react-dom", "@primer/react"],
      loader: {
        '.ttf': 'file',
      },
    });
  });

  try {
    await Promise.all(blockBuildFuncs);
  } catch (e) {
    console.error("Error bundling blocks", e);
  }
}
build()

module.exports = build;

Introduction to the Implementation Principle of Github Blocks#

As mentioned earlier, in the process of developing Blocks, we only need to expose a React component externally, and when finally packaging, React-related libraries are excluded. So how is it loaded in the end?

By examining the source code, we can see that the entire loading of the Block is actually loading an iframe tag. The hash value on the iframe src stores the source information of the Block to be loaded. Then there will be a piece of runtime code inside, and this code has also been open-sourced.

<iframe class="w-full h-full" allow="camera;microphone;xr-spatial-tracking" sandbox="allow-scripts allow-same-origin allow-forms allow-top-navigation-by-user-activation allow-popups" src="https://blocks-sandbox.githubnext.com#%7B%22block%22%3A%7B%22type%22%3A%22folder%22%2C%22id%22%3A%22dashboard%22%2C%22title%22%3A%22Dashboard%22%2C%22description%22%3A%22View%20other%20blocks%20in%20a%20dashboard%20view%22%2C%22entry%22%3A%22blocks%2Ffolder-blocks%2Fdashboard%2Findex.tsx%22%2C%22example_path%22%3A%22https%3A%2F%2Fgithub.com%2Fgithubnext%2Fblocks-tutorial%22%2C%22owner%22%3A%22githubnext%22%2C%22repo%22%3A%22blocks-examples%22%7D%2C%22context%22%3A%7B%22repo%22%3A%22blocks%22%2C%22owner%22%3A%22githubnext%22%2C%22path%22%3A%22docs%2FDeveloping%20blocks%22%2C%22sha%22%3A%22main%22%7D%7D"></iframe>

By reading the code, we can know that when the Block is loaded, it will first load this runtime code. The runtime code is responsible for communicating with the main page. When the page is loaded, it will send the loaded message to the main page, and then the main page will send the relevant information (the content of the file currently being viewed, and the code for loading the Block) over. At this time, the runtime will load our Block. The specific loading method is to turn our code into a script tag and append it to the DOM tree:

const loadReactContent = (content: string) => {
  return `
var BlockBundle = ({ React, ReactJSXRuntime, ReactDOM, ReactDOMClient, PrimerReact }) => {
  function require(name) {
    switch (name) {
      case "react":
        return React;
      case "react/jsx-runtime":
        return ReactJSXRuntime;
      case "react-dom":
        return ReactDOM;
      case "react-dom/client":
        return ReactDOMClient;
      case "@primer/react":
      case "@primer/components":
        return PrimerReact;
      default:
        console.log("no module '" + name + "'");
        return null;
    }
  }
${content}
  return BlockBundle;
};`;
};

From the code, we can see that the excluded library files are supplemented through runtime injection. This can minimize the size of the business code. At the same time, the runtime is reusable because when you switch Blocks, it is essentially switching the hash of the URL, which does not trigger a complete reload of the iframe. When the page inside the iframe detects a change in the hash, it will request the relevant information from the main page to complete the next rendering. Therefore, the difference is the size of your business code package.

After appending it to the DOM tree, the window object will have the function BlockBundle, and the runtime will call and execute the code to render the Block, completing the initial rendering.

By reading the code of the Runtime, we can also know that Blocks provides development methods for other technology stacks, but the global variable name should be VanillaBlockBundle. The official also provides Vue and Svelte template code. From the template code, we can see that the official hopes you will use the React technology stack, after all, Vue and Svelte need to bring in an additional runtime for the corresponding library, which may affect performance.

Summary#

This article introduces Github Blocks from usage to development to implementation. In fact, Github has already provided code viewing, code editing, and version management. If you can use Blocks to load and display code online, you can already implement a rudimentary version of a Cloud IDE (lacking features such as Terminal and Extensions). However, this feature itself overlaps with many products on the market (stackblitz, codesandbox), so it seems that implementing this feature does not make much sense. The overall experience of the functionality may be relatively mediocre because you still need to go to blocks.githubnext.com to experience it. Of course, this feature may be temporarily introduced in this way because it is not public. The overall interaction may feel heavy, and frequent switching of Blocks may feel poor in performance. I have not seen whether it is possible to fix a Folder Block for display, so it is not possible to change other code and refresh the entire project-related content (perhaps needing a Project Block?). There will be a process of switching and loading Blocks back and forth. Currently, I can't think of more imaginative Blocks, so I can only look forward to the ideas from the community in the future.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.