HtmlGenerator.js 4.06 KB
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { cheerio } = require('@umijs/utils');
class MyPlugin {
  constructor(opts) {
    this.opts = opts;
  }

  getAsset(opts) {
    if (/^(https|http)?:\/\//.test(opts.file)) {
      return opts.file;
    }
    // const file = opts.file.charAt(0) === '/' ? opts.file.slice(1) : opts.file;
    // return `${this.config.publicPath}${file}`;
  }

  getStyles(option) {
    const linkArr = [];
    const styleObj = [];
    if (Array.isArray(option) && option.length > 0) {
      option.forEach(style => {
        if (typeof style === 'string') {
          if (EXP_URL.test(style)) {
            // is <link />
            linkArr.push({
              charset: 'utf-8',
              rel: 'stylesheet',
              type: 'text/css',
              href: style,
            });
          } else {
            styleObj.push({
              content: style,
            });
          }
        }
        if (typeof style === 'object') {
          // is style object
          styleObj.push(style);
        }
      });
    }
    return [linkArr, styleObj];
  }

  getScriptsContent(scripts) {
    return scripts
      .map(script => {
        const { content, ...attrs } = script;
        if (content && !attrs.src) {
          const newAttrs = Object.keys(attrs).reduce((memo, key) => {
            return [...memo, `${key}="${attrs[key]}"`];
          }, []);
          return [
            `<script${newAttrs.length ? ' ' : ''}${newAttrs.join(' ')}>`,
            content
              .split('\n')
              .map(line => `  ${line}`)
              .join('\n'),
            '</script>',
          ].join('\n');
        } else {
          const newAttrs = Object.keys(attrs).reduce((memo, key) => {
            return [...memo, `${key}="${attrs[key]}"`];
          }, []);
          return `<script ${newAttrs.join(' ')}></script>`;
        }
      })
      .join('\n');
  }

  getContent(html, config) {
    let {
      metas = [],
      links = [],
      headScripts = [],
      scripts = [],
    } = config;

    const $ = cheerio.load(html, {
      decodeEntities: false,
    });
    metas.forEach(meta => {
      $('head').append(
        [
          '<meta',
          ...Object.keys(meta).reduce((memo, key) => {
            return memo.concat(`${key}="${meta[key]}"`);
          }, []),
          '/>',
        ].join(' '),
      );
    });

    links.forEach(link => {
      $('head').append(
        [
          '<link',
          ...Object.keys(link).reduce((memo, key) => {
            return memo.concat(`${key}="${link[key]}"`);
          }, []),
          '/>',
        ].join(' '),
      );
    });

    const [linkArr = [], styleArr = []] = this.getStyles(this.opts.config.styles);
    styleArr.forEach(style => {
      const { content = '', ...attrs } = style;
      const newAttrs = Object.keys(attrs).reduce((memo, key) => {
        return memo.concat(`${key}="${attrs[key]}"`);
      }, []);
      $('head').append(
        [
          `<style${newAttrs.length ? ' ' : ''}${newAttrs.join(' ')}>`,
          content
            .split('\n')
            .map(line => `  ${line}`)
            .join('\n'),
          '</style>',
        ].join('\n'),
      );
    });

    if (headScripts.length) {
      $('head').append(this.getScriptsContent(headScripts));
    }

    if (scripts.length) {
      $('body').append(this.getScriptsContent(scripts));
    }

    linkArr.forEach(file => {
      $('head').append(
        `<link rel="stylesheet" href="${this.getAsset({
          file: file.href,
        })}" />`,
      );
    });

    html = $.html();
    return html; 
  }

  apply(compiler) {
    compiler.hooks.compilation.tap('MyPlugin', compilation => {
      HtmlWebpackPlugin.getHooks(compilation).afterTemplateExecution.tapAsync(
        'MyPlugin', // <-- Set a meaningful name here for stacktraces
        (data, cb) => {
          if (data && data.html !== '') {
            data.html = this.getContent(data.html, this.opts.config);
          }
          // Tell webpack to move on
          cb(null, data);
        },
      );
    });
  }
}

module.exports = MyPlugin;