The following sample code, which may contain multiple file code, is a bit messy. Umi/AF-Webpack was used for scaffolding construction and Vuant-UI was used for document layout style.

Thoughts together

  • Implement a generic scaffolding, complete initialization of the project, build, and publish component documentation.
  • Webpack was used to build the scaffolding because the documentation site is really an application
  • Components need to have preview function, so we can adopt MPA multi-entry packaging
  • A configuration file is agreed, in which the header and sideBar of the main configuration document are configured. This part is also equivalent to configuring route
  • Use require.context to load the md file in the user directory

Begin to implement

The scaffold

Initialize the

Basically, the CLI has a template built in, and you can choose to have multiple fixed templates, or you can dynamically generate a personalized initial template individually through the init phase with options, technical solution: Inquirer + EJS or Mustache

Sample code:

async function init() {
  try {
    const answers = await inquirer.prompt(questions);

    const { projectName, platform, framework, attributes, bu, site } = answers;

    const templateRoot = path.join(__dirname, './boilerplate');

    const projectPath = path.join(cwd, projectName);

    await fse.copySync(templateRoot, projectPath, {
      filter: (file) = > {
        const reg = /\.(tpl|ejs)$/ig;
        if (reg.test(file)) {
          return false;
        }
        return true; }});const code = fse.readFileSync(path.join(templateRoot, 'config.ejs'), {
      encoding: 'utf8'});await fse.writeFileSync(
      path.join(projectPath, 'config.js'),
      ejs.render(
        code,
        {
          projectName,
          platform,
          framework,
          attributes,
          bu,
          site,
        },
      ),
      {
        encoding: 'utf8',}); signale.success(chalk.cyan('Project created successfully! '));
    signale.info(chalk.cyan('NPM run dev Let's get to work! ! 😝 '));
  } catch(error) { signale.error(error); }}Copy the code

build

The scaffolding construction function uses Webpack. In order to achieve it quickly, the af-Webpack configuration scheme within UMI is directly adopted and twice encapsulated 👍. When the scaffolding construction command is executed, relevant parameters of the local configuration are transparently transmitted to AF-Webpack, basically with a green light.

  • Configuring Multiple Entries
  entry: {
    'docs': [
      webpackHotDevClientPath, // Open HMR
      path.join(paths.absTmpDirPath, './docs/index.js')],'preview': [
      webpackHotDevClientPath,
      path.join(paths.absTmpDirPath, './preview/index.js'),],}Copy the code
  • Markdown parsing

In order to use markdown as a component output, keep the normal markdown-loader function and customize the loader slightly, so that front-matter configuration information can be directly displayed on the page, as follows:

function transformToComponent(content, fm) {
  const { attributes } = fm;
  return `
    import React, { Component, Fragment } from 'react';

    export default class extends Component {

      state = {
        html: 'The ${escape(content)}',
        attr: The ${JSON.stringify(attributes)},
      }

      render() {
        const { html, attr } = this.state;
        const { platform, framework, attributes } = attr;
        return (
          <Fragment>
            {/*<section>
              <blockquote>
                <ul>
                  {platform && <li>Platform: {platform}</li>}
                  {framework && <li>Framework: {framework}</li>}
                  {attributes && <li>Attributes: {attributes}</li>}
                </ul>
              </blockquote>
            </section>*/}
            <section dangerouslySetInnerHTML={{ __html: unescape(html) }} />
          </Fragment>
        )
      }
    }
  `;
}

Copy the code

release

todo…

Example of a convention configuration file:

module.exports = {
  header: {
    logo: {
      image: 'https://www.baidu.com'.title: 'YufPress'.href: '# /'}},// siderBar is a route that is resolved to a child route of the react-router
  sideBar: {
    name: 'UI'.groups: [{groupName: 'Base components'.list: [{disabled:  false.enablePreview: true.// Whether to enable preview
            path: '/Alert'.// Path (that is, the path under the docs folder
            title: 'Alert',},]}]},};Copy the code

Example code for build time processing is as follows:

// Collect files
const docsMap = {};
const req = require.context('@root/docs'.true, /\.md$/);

req.keys().forEach((key) = > {
  docsMap[key] = req(key);
});

/ / generates the route

import { sideBar } from '@root/config';
const routes = []; // Run the react-router command to generate a route

sideBar.groups.forEach((group) = > {
  group.list.forEach((page) = > {
    addRoute(page);
  });
});


// addRoute

function addRoute(page) {
  const { path, title, disabled = false } = page;
  if (path) {
    // Compatible processing
    const module =
      componentMap[`.${path}/readme.md`] ||
      componentMap[`.${path}/README.md`] ||
      componentMap[`.${path}.md`) | | {};if(! disabled) { routes.push({component: module.default || None,
        name: `${path}`.path: ` /${path}`, title, }); }}}Copy the code

By the same token, the preview component (. (j | t) sx?) This file is the same way

conclusion

I started thinking, why am I writing this? I don’t think there’s much to write about. The next article will be about how I implemented the plug-in mechanism on this basis.