hexo/helper/cdn.js

/**
 * Register the Hexo helper functions for resolving the URL of static files of JavaScript libraries,
 * web fonts, and FontAwesome icon fonts.
 * @module hexo/helper/cdn
 */

/**
 * @private
 */
const PROVIDERS = {
  LIBRARY: {
    cdnjs: '[cdnjs]https://cdnjs.cloudflare.com/ajax/libs/${ package }/${ version }/${ filename }',
    loli: '[cdnjs]https://cdnjs.loli.net/ajax/libs/${ package }/${ version }/${ filename }',
    jsdelivr: 'https://cdn.jsdelivr.net/npm/${ package }@${ version }/${ filename }',
    unpkg: 'https://unpkg.com/${ package }@${ version }/${ filename }',
  },
  FONT: {
    google: 'https://fonts.googleapis.com/${ type }?family=${ fontname }',
    loli: 'https://fonts.loli.net/${ type }?family=${ fontname }',
    fontim: 'https://fonts.font.im/${ type }?family=${ fontname }',
    ustc: 'https://fonts.lug.ustc.edu.cn/${ type }?family=${ fontname }',
  },
  ICON: {
    loli: 'https://cdnjs.loli.net/ajax/libs/font-awesome/6.0.0/css/all.min.css',
    fontawesome: 'https://use.fontawesome.com/releases/v6.0.0/css/all.css',
  },
};

/**
 * Convert npm library path to CDN.js path
 * @private
 */
const CDNJS_FIXTURES = {
  moment: (ver, fname) => ['moment.js', ver, fname.startsWith('min/') ? fname.substr(4) : fname],
  outdatedbrowser: (ver, fname) => [
    'outdated-browser',
    ver,
    fname.startsWith('outdatedbrowser/') ? fname.substr(16) : fname,
  ],
  'highlight.js': (ver, fname) => [
    'highlight.js',
    ver,
    fname.endsWith('.css') && fname.indexOf('.min.') === -1
      ? fname.substr(0, fname.length - 4) + '.min.css'
      : fname,
  ],
  mathjax: (ver, fname) => [
    'mathjax',
    ver,
    (() => {
      fname = fname.startsWith('unpacked/') ? fname.substr(9) : fname;
      return fname.indexOf('.min.') > -1 ? fname.replace(/\.min\./gi, '.') : fname;
    })(),
  ],
  katex: (ver, fname) => ['KaTeX', ver, fname],
  'pace-js': (ver, fname) => ['pace', ver, fname],
  clipboard: (ver, fname) => ['clipboard.js', ver, fname],
  cookieconsent: (ver, fname) => [
    'cookieconsent',
    ver,
    fname.startsWith('build/') ? fname.substr(6) : fname,
  ],
  '@waline/client': (ver, fname) => ['waline', ver, fname],
};

const UNPKG_FIXTURES = {
  mathjax: (ver, fname) => [
    'mathjax',
    ver,
    fname.indexOf('.min.') > -1 ? fname.replace(/\.min\./gi, '.') : fname,
  ],
  disqusjs: (ver, fname) => [
    'disqusjs',
    ver,
    fname.indexOf('.min.') > -1 ? fname.replace(/\.min\./gi, '.') : fname,
  ],
};

/**
 * Register the Hexo helper functions for resolving the URL of static files of JavaScript libraries,
 * web fonts, and FontAwesome icon fonts.
 *
 * @param {Hexo} hexo The Hexo instance.
 * @example
 * // Use the function below to resolve the CDN URL JavaScript library static files in your view files
 * cdn('jquery', '3.3.1', 'dist/jquery.min.js');
 * // -> https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js
 *
 * // Use the function below to resolve the CDN URL web font files
 * fontcdn('Ubuntu:400,600|Source+Code+Pro', 'css');
 * // -> https://fonts.googleapis.com/css?family=Ubuntu:400,600|Source+Code+Pro
 *
 * // Use the function below to insert FontAwesome icon font CSS URL.
 * iconcdn();
 * // -> https://use.fontawesome.com/releases/v5.15.2/css/all.css
 */
module.exports = function (hexo) {
  function applyFixture(fixture, _package, version, filename) {
    if (Object.prototype.hasOwnProperty.call(fixture, _package)) {
      const result = fixture[_package](version, filename);
      // package is not hosted on the given CDN
      if (!result.length) {
        throw new Error();
      }
      return result;
    }
    return [_package, version, filename];
  }

  hexo.extend.helper.register('cdn', function (_package, version, filename) {
    const { cdn = 'jsdelivr' } =
      typeof this.config.providers === 'object' ? this.config.providers : {};
    let _cdn = cdn;
    if (_cdn in PROVIDERS.LIBRARY) {
      _cdn = PROVIDERS.LIBRARY[_cdn];
    }
    // cdn.js does not follow a GitHub npm style like jsdeliver and unpkg do. Patch it!
    if (_cdn.startsWith('[cdnjs]')) {
      _cdn = _cdn.substr(7);
      try {
        [_package, version, filename] = applyFixture(CDNJS_FIXTURES, _package, version, filename);
        if (filename.startsWith('dist/')) {
          filename = filename.substr(5);
        }
      } catch (e) {
        _cdn = PROVIDERS.LIBRARY.jsdelivr;
      }
    } else if (cdn === 'unpkg') {
      try {
        [_package, version, filename] = applyFixture(UNPKG_FIXTURES, _package, version, filename);
      } catch (e) {
        _cdn = PROVIDERS.LIBRARY.jsdelivr;
      }
    }
    return _cdn
      .replace(/\${\s*package\s*}/gi, _package)
      .replace(/\${\s*version\s*}/gi, version)
      .replace(/\${\s*filename\s*}/gi, filename);
  });

  hexo.extend.helper.register('fontcdn', function (fontName, type = 'css') {
    let { fontcdn = 'google' } =
      typeof this.config.providers === 'object' ? this.config.providers : {};
    if (fontcdn in PROVIDERS.FONT) {
      fontcdn = PROVIDERS.FONT[fontcdn];
    }
    return fontcdn.replace(/\${\s*fontname\s*}/gi, fontName).replace(/\${\s*type\s*}/gi, type);
  });

  hexo.extend.helper.register('iconcdn', function () {
    let { iconcdn = 'fontawesome' } =
      typeof this.config.providers === 'object' ? this.config.providers : {};
    if (iconcdn in PROVIDERS.ICON) {
      iconcdn = PROVIDERS.ICON[iconcdn];
    }
    return iconcdn;
  });
};