7

Preface

When it comes to front-end components of the field, under Vue technology system has Element the UI , have lower React technology system Ant Design , these are the current front-end siege lion who will inevitably be practical to use the base component library. In actual work, we will inevitably have to organize a set of component libraries that suit our business style according to our work content. The basic components can be based on the above open source component libraries and multi-theme style solutions such as the less framework. Customization, but more is based on these basic components to organize a set of business component libraries suitable for their business products.

When it comes to developing component libraries, we may choose Monorepo single warehouse multi-package form (refer to the web document https://segmentfault.com/a/1190000019309820 etc.) or other Git multi-warehouse single package form to maintain the component code, In the end, it is inevitable to put the components into a document and provide them to other colleagues for reference.

This article is about the production of component documents, and I will talk about a series of thinking processes in the process of producing documents to solve the experience problem in the "last mile" of component development.

Component documentation

Investigation of specifications and construction

Component documentation must have certain specifications, and specifications are the first step in any software engineering phase. For components, what content should be included in the contents of the document determines all the experience of both the component developer and the user. It is not difficult to determine these specifications. Refer to the documentation of the open source component library, and we will have some initial impressions.

Because our team uses the React technology stack, here we refer to the Ant Design component library.

For example, the most basic Button component , the content structure of the official document from top to bottom is as follows:

  1. The title of the component is displayed, followed by a brief description of the component.
  2. List the usage scenarios of the component.
  3. Effect demonstrations and code cases in different usage scenarios. You can jump to CodeSandbox, CodePen and other online source code editor websites to edit and view the effect in real time.
  4. List the configurable properties and interface methods of the component. The list contains attribute/method name, field type, default value, usage description, etc.
  5. Frequently asked questions FAQ.
  6. Links to some case instructions for designers.

These documents are very rich in content. As an open component library, almost all aspects from design to development perspective are considered, and the user experience is particularly good. Driven by curiosity, I checked the official website source code library, such as Button component: https://github.com/ant-design/ant-design/tree/master/components/button . Under the source code library, there are placed .zh-CN.md and .en-US.md , and among these Markdown files, is the content of the official website documents that we see...Huh? No, it seems that something is missing. What about the case demonstration and sample code?

Is the AntD official website document developed and maintained manually? . According to its official website, which is similar to 161b2ecf865ee8 docs/react/introduce-cn , there is a corresponding Markdown file in the source code library. The official website documents must be the official warehouse according to a specification. Generated. So how is it generated? It was the first time for me to do the component documentation specification and I was aroused.

As a veteran of front-end engineering, I skillfully opened its package.json file. By checking the scripts command, I easily found the site command ( source code warehouse package.json ).

npm run site:theme && cross-env NODE_ICU_DATA=node_modules/full-icu ESBUILD=1 bisheng build --ssr -c ./site/bisheng.config.js

It turns out that the construction of the website used bisheng . By consulting and understanding bisheng , I found that it is indeed an automatic generation tool for the file system. There is a plug-in bisheng-plugin-react , which can convert the JSX source code block in the Markdown document into a demo that can be run. Example. And the sample code document of each component itself is in the path of each component
under the 161b2ecf86602a demo

Emmm, bisheng is indeed very good and powerful, and can also support multiple languages, combined with certain document specification constraints, can quickly build a document master station. However, in the process of in-depth understanding of bisheng , I found that its documentation is relatively lacking, there are so many things to package, and the sense of black box is serious in the use process, and the component library of our team is actually very simple. One is able to do it. To facilitate circulation, but only for internal circulation and use, not open source. So, is there an easier way to build a document?

More research on the documentation tool library

Typing in Google search keywords such as React Components Documentation, we will soon be able to search out a lot of documents related to React assembly tool library, where I saw these as follows: Docz , Storybook , React Styleguidist dumi under the UMI construction system and so on.

These tool libraries all support the parsing of Markdown documents. Among them, Docz and StoryBook also support the use of mdx format (a hybrid of Markdown and JSX), and the document content format can support functions such as component attribute lists and sample code demonstrations.

Next, let's take a brief look at the support of these tool libraries for component documents.

Docz

In the process of understanding, I found that Docz is actually a relatively old file system building tool. It itself mainly promotes documents in MDX format, and it can run without any configuration. It supports local debugging and construction to generate releasable products, supports multi-package warehouse, TypeScript environment, CSS processor, plug-in mechanism, etc., which fully meets the functional needs.

Only Docz seems to only support React components (of course it is enough for us), and its NPM package was recently updated two years ago. In addition, although the MDX format document has a low cost of understanding, it still has a certain acceptance and proficiency cost for colleagues who do not use it much. Temporary alternative.

StoryBook

When I first learned about StoryBook , I was surprised by its 66.7K Star volume (Docz is 22K). Compared with Docz, StoryBook very rich community content. It does not rely on the technology stack system of components. Support dozens of technology stacks such as React, Vue, Angular, Web Components, etc.

StoryBook The way to build a document system is not to automatically parse Markdown files, but to expose a series of document building interfaces, allowing developers to manually write stories files for the components themselves. StoryBook will automatically parse these stories files to generate Document content. This approach will bring a certain cost of learning and understanding the interface, but at the same time it also achieves the effect of supporting the cross-component technology stack based on this approach, and makes the community more abundant.

Official example: https://github.com/storybookjs/storybook/tree/next/examples .

StoryBook is unquestionably powerful, but for our team's situation, it is still a bit of a sledgehammer. In addition, it requires additional understanding of interface functions and writing component stories files that are difficult to promote within the team: everyone is very busy, component development is distributed among dozens of people in the team, the situation is more complicated, and the document collation is restricted to one person. Unrealistic. Continue to research.

React Styleguidist

React Styleguidist is not as dazzling as StoryBook (10K+), but the download volume of the package body is also relatively large, and recent submissions are also quite active. From the name, it supports the environment of React components. It generates the document by automatically parsing the Mardown file. The implementation method is to automatically parse the JSX declaration code block in the document, find the component source code according to the one-to-one correspondence rule of the name, and then package the declared code block through Webpack to generate the corresponding Demonstration example.

After continuing to try React Styleguidist , one of its features made my eyes shine: it will automatically parse the properties of the component, and parse out its type, default value, comment description, etc., and then parse it The content is automatically generated and the attribute table is placed above the demo example. This is a bit JSDOC . For a component developer, TA really needs to care about the disclosure of component properties, comments, and the writing of document cases, but it is enough after writing it, and there is no need to think about how to adapt to build out A file system.

In addition, React Styleguidist resolves component properties based on the analysis of AST and the tool react-docgen . It also supports react-docgen-typescript to realize the analysis of components in many configurations in the environment of 161b2ecf8664d4. Item supports changing the display style and content format of various parts related to the document site, and the configuration customization support is quite flexible.

Of course, it also has some shortcomings, such as embedded Webpack, which is an additional configuration burden for the situation where Rollup.js

In general, React Styleguidist in my opinion is a small and beautiful tool library, which is very suitable for our teamwork with a large number of participants and most of the daily development work is heavy. Temporary alternative.

dumi

I understand that dumi is because some component documentation sites in our team have been built based on it. dumi also implements the establishment of a document system by automatically parsing Markdown documents. It is also basically zero-configuration, and there are many flexible configurations that support changing the display content and (theme) styles of some parts of the document site. It inherits the UMI system as a whole. Style: Out of the box, excellent packaging. It can be used alone or combined with UMI framework to configure and use.

It’s just that compared to the React Styleguidist that I have learned above, I don’t see other obvious advantages, and it seems that I don’t see the function of automatically parsing component properties. For me, there is no React Styleguidist . Got some highlights. You can refer to it and don't consider it anymore.

Component documentation generation

After comparing the tool library built by multiple documents, I finally chose React Styleguidist . In my opinion, it is naturally based on react-docgen to realize the function of parsing component properties, types, annotation descriptions, etc., which attracts me. On the one hand, this function can standardize the development of components by team colleagues with less extra time. On the other hand, the access form of its API interface can uniformly output the format and style of document content through unified construction and configuration, which is convenient for the access and use of various services.

After deciding on the technical solution, it is how to specifically implement a tool based on it to facilitate the access of various business warehouses.

We have our own team to build a unified CLI tool, one more React Styleguidist CLI configuration will have some familiarity with the cost in the understanding, but I can based on React Styleguidist the Node API access form, will React Styleguidist The functions are integrated into our own CLI's dev and build commands.

First, based on React Styleguidist API, a unified set of configurations will be generated to abstract the code for the React Styleguidist

// 定义一套统一的配置,生成 react-styleguidist 实例
import styleguidist from 'react-styleguidist/lib/scripts/index.esm';
import * as docgen from 'react-docgen';
import * as docgenTS from 'react-docgen-typescript';

import type * as RDocgen from 'react-docgen';

export type DocStyleguideOptions = {
  cwd?: string;
  rootDir: string;
  workDir: string;
  customConfig?: object;
};

const DOC_STYLEGUIDE_DEFAULTS = {
  cwd: process.cwd(),
  rootDir: process.cwd(),
  workDir: process.cwd(),
  customConfig: {},
};

export const createDocStyleguide = (
  env: 'development' | 'production',
  options: DocStyleguideOptions = DOC_STYLEGUIDE_DEFAULTS,
) => {
  // 0. 处理配置项
  const opts = { ...DOC_STYLEGUIDE_DEFAULTS, ...options };
  const {
    cwd: cwdPath = DOC_STYLEGUIDE_DEFAULTS.cwd,
    rootDir,
    workDir,
    customConfig,
  } = opts;

  // 标记:是否正在调试所有包
  let isDevAllPackages = true;

  // 解析工程根目录包信息
  const pkgRootJson = Utils.parsePackageSync(rootDir);

  // 1. 解析指定要调试的包下的组件
  let componentsPattern: (() => string[]) | string | string[] = [];
  if (path.relative(rootDir, workDir).length <= 0) {
    // 选择调试所有包时,则读取根路径下 packages 字段定义的所有包下的组件
    const { packages = [] } = pkgRootJson;
    componentsPattern = packages.map(packagePattern => (
      path.relative(cwdPath, path.join(rootDir, packagePattern, 'src/**/[A-Z]*.{js,jsx,ts,tsx}'))
    ));
  } else {
    // 选择调试某个包时,则定位至选择的具体包下的组件
    componentsPattern = path.join(workDir, 'src/**/[A-Z]*.{js,jsx,ts,tsx}');
    isDevAllPackages = false;
  }

  // 2. 获取默认的 webpack 配置
  const webpackConfig = getWebpackConfig(env);

  // 3. 生成 styleguidist 配置实例
  const styleguide = styleguidist({
    title: `${pkgRootJson.name}`,
    // 要解析的所有组件
    components: componentsPattern,
    // 属性解析设置
    propsParser: (filePath, code, resolver, handlers) => {
      if (/\.tsx?/.test(filePath)) {
        // ts 文件,使用 typescript docgen 解析器
        const pkgRootDir = findPackageRootDir(path.dirname(filePath));
        const tsConfigParser = docgenTS.withCustomConfig(
          path.resolve(pkgRootDir, 'tsconfig.json'),
          {},
        );
        const parseResults = tsConfigParser.parse(filePath);
        const parseResult = parseResults[0];
        return (parseResult as any) as RDocgen.DocumentationObject;
      }
      // 其他使用默认的 react-docgen 解析器
      const parseResults = docgen.parse(code, resolver, handlers);
      if (Array.isArray(parseResults)) {
        return parseResults[0];
      }
      return parseResults;
    },
    // webpack 配置
    webpackConfig: { ...webpackConfig },
    // 初始是否展开代码样例
    // expand: 展开 | collapse: 折叠 | hide: 不显示;
    exampleMode: 'expand',
    // 组件 path 展示内容
    getComponentPathLine: (componentPath) => {
      const pkgRootDir = findPackageRootDir(path.dirname(componentPath));
      try {
        const pkgJson = Utils.parsePackageSync(pkgRootDir);
        const name = path.basename(componentPath, path.extname(componentPath));
        return `import ${name} from '${pkgJson.name}';`;
      } catch (error) {
        return componentPath;
      }
    },
    // 非调试所有包时,不显示 sidebar
    showSidebar: isDevAllPackages,
    // 日志配置
    logger: {
      // One of: info, debug, warn
      info: message => Utils.log('info', message),
      warn: message => Utils.log('warning', message),
      debug: message => console.debug(message),
    },
    // 覆盖自定义配置
    ...customConfig,
  });

  return styleguide;
};

In this way, under the dev and build server interface method and the build interface method of the instance can be called respectively to realize debugging and build static resources of the output document.

// dev 命令下启动调试
// 0. 初始化配置
const HOST = process.env.HOST || customConfig.serverHost || '0.0.0.0';
const PORT = process.env.PORT || customConfig.serverPort || '6060';

// 1. 生成 styleguide 实例
const styleguide = createDocStyleguide(
  'development',
  {
    cwd: cwdPath,
    rootDir: pkgRootPath,
    workDir: workPath,
    customConfig: {
      ...customConfig,
      // dev server host
      serverHost: HOST,
      // dev server port
      serverPort: PORT,
    },
  },
);

// 2. 调用 server 接口方法启动调试
const { compiler } = styleguide.server((err, config) => {
  if (err) {
    console.error(err);
  } else {
    const url = `http://${config.serverHost}:${config.serverPort}`;
    Utils.log('info', `Listening at ${url}`);
  }
});
compiler.hooks.done.tap('done', (stats: any) => {
  const timeStr = stats.toString({
    all: false,
    timings: true,
  });

  const statStr = stats.toString({
    all: false,
    warnings: true,
    errors: true,
  });

  console.log(timeStr);

  if (stats.hasErrors()) {
    console.log(statStr);
    return;
  }
});
// build 命令下执行构建

// 生成 styleguide 实例
const styleguide = MonorepoDev.createDocStyleguide('production', {
  cwd,
  rootDir,
  workDir,
  customConfig: {
    styleguideDir: path.join(pkgDocsDir, 'dist'),
  },
});
// 构建文档内容
await new Promise<void>((resolve, reject) => {
  styleguide.build(
    (err, config, stats) => {
      if (err) {
        reject(err);
      } else {
        if (stats != null) {
          const statStr = stats.toString({
            all: false,
            warnings: true,
            errors: true,
          });
          console.log(statStr);
          if (stats.hasErrors()) {
            reject(new Error('Docs build failed!'));
            return;
          }
          console.log('\n');
          Utils.log('success', `Docs published to ${path.relative(workDir, config.styleguideDir)}`);
        }
        resolve();
      }
    },
);

package.json under each package of the component multi-package warehouse, configure the dev and build commands respectively. Realized support for non-sense startup debugging and construction of output document resources.

summary

This article mainly introduces my thinking process in the process of investigating and implementing the component document specification and construction. As mentioned in the article when introducing other document system construction tools, there are many excellent open source tools that can support the realization of the desired effect. This is The front-end siege lions are lucky and unfortunate: we can stand on the shoulders of our predecessors, but to choose a suitable one from so many excellent libraries, we need to do more understanding and benefit points. An old saying lasts forever: The best that suits you is the best.

Hope this article can be helpful to you who read here.

Author: ES2049 / Jin Zhikai
The article can be reprinted at will, but please keep this link to the original text.
You are very welcome to join ES2049 Studio if you are passionate. Please send your resume to caijun.hcj@alibaba-inc.com.

ES2049
3.7k 声望3.2k 粉丝