前言

solitude主题的配置项可以自定义外部调用的地址,但是一直用别人的加速,就怕有一天出现问题导致还需要重新替换

所以打算弄成本地话,本教程基于在 Solitude 上优雅的使用 CDN 加速静态资源文件修改

教程

本教程适用的版本为v3.0.20,若有不同,请自行修改脚本代码

首先就是在根目录创建一个名为plugins.js的文件,然后将如下代码放进去,其中的isNPMfoderName根据实际情况修改

#!/usr/bin/env node
/**
 * plugins.js
 * 用于下载 Hexo 主题 Solitude 中第三方的插件文件。
 * 该脚本会读取主题目录下的 plugins.yml 文件,解析其中的插件信息,
 * 并从 CDN 上下载相应的文件到指定的本地目录。
 * node plugins.js
 */

// 变量
const isNPM = false; // 是否是 NPM 安装
const foderName = 'plugins'; // 存储文件夹名称

// 模块
const fs = require('fs');
const fsPromises = require('fs/promises');
const path = require('path');
const https = require('https');

const THEME_DIR = isNPM ? path.join(__dirname, 'node_modules', 'hexo-theme-solitude') : path.join(__dirname, 'themes', 'solitude');
const PLUGIN_FILE = path.join(THEME_DIR, 'plugins.yml');
const OUTPUT_DIR = path.join(__dirname, foderName);

function parsePluginsYml(text) {
  const res = {};
  let cur = null;
  text.split(/\r?\n/).forEach(l => {
    const ln = l.split('#')[0].trimEnd();
    if (!ln) return;
    const sec = ln.match(/^([a-zA-Z0-9_\-]+):\s*$/);
    if (sec) {
      cur = sec[1];
      res[cur] = {};
      return;
    }
    const kv = ln.match(/^  ([a-zA-Z0-9_\-]+):\s*(.*)$/);
    if (kv && cur) {
      res[cur][kv[1]] = kv[2].trim();
    }
  });
  return res;
}

async function mkdirp(d) {
  try {
    await fsPromises.mkdir(d, { recursive: true });
  } catch (e) {
    if (e.code !== 'EEXIST') throw e;
  }
}

function fetch(url, dest) {
  return new Promise((resolve, reject) => {
    const request = https.get(url, (response) => {
      // 处理重定向
      if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
        response.destroy();
        return fetch(response.headers.location, dest).then(resolve, reject);
      }

      if (response.statusCode !== 200) {
        response.destroy();
        return reject(new Error(`${response.statusCode} ${url}`));
      }

      const fileStream = fs.createWriteStream(dest);
      response.pipe(fileStream);

      fileStream.on('finish', () => {
        fileStream.close(resolve);
      });

      fileStream.on('error', (err) => {
        fs.unlink(dest, () => reject(err));
      });
    }).on('error', (err) => {
      reject(err);
    });
  });
}

function minFile(f) {
  return f.replace(/(?<!\.min)\.(js|css)$/i, '.min.$1');
}

function stripDistSegments(f) {
  // 移除任意位置的 dist/ 目录段(包含开头或中间)
  return f.replace(/(^|\/)dist\//g, '$1');
}

(async () => {
  try {
    const pluginsText = await fsPromises.readFile(PLUGIN_FILE, 'utf8');
    const plugins = parsePluginsYml(pluginsText);
    await mkdirp(OUTPUT_DIR);

    for (const [key, def] of Object.entries(plugins)) {
      if (!def.name || !def.file || !def.version) {
        console.warn(`[SKIP] ${key} 缺少 name/file/version`);
        continue;
      }

      const { name, file, version, other_name } = def;
      
      const dirName = `${other_name || name}@${version}`;

      const cdnFilePath = minFile(stripDistSegments(file));
      const url = `https://cdnjs.cloudflare.com/ajax/libs/${other_name || name}/${version}/${cdnFilePath}`;
      
      const dest = path.join(OUTPUT_DIR, dirName, stripDistSegments(file));
      
      try {
        await fsPromises.access(dest);
        console.log(`[SKIP] ${key}`);
        continue;
      } catch (e) {
        if (e.code !== 'ENOENT') {
          console.error(`[ERROR] ${key} 访问文件失败: ${e.message}`);
          continue;
        }
      }

      console.log(`[DOWN] ${key} → ${dest}`);
      try {
        await mkdirp(path.dirname(dest));
        await fetch(url, dest);
        console.log(`[OK]   ${key}`);
      } catch (e) {
        console.error(`[FAIL] ${key}: ${e.message}`);
      }
    }
    console.log('全部完成!');
  } catch (error) {
    console.error(`脚本执行失败: ${error.message}`);
  }
})();

然后就是运行此脚本,在终端输入node plugins.js即可下载文件到plugins文件夹中

最后就是修改调用方式了

找到_config.solitude.yml中的CDN类,将third_party改成custom

然后将custom_format改成/plugins/${cdnjs_name}@${version}/${min_cdnjs_file}即可。

有些朋友就会发现控制台会报缺少字体文件,这时候我们需要手动下载这两个字体文件并且放到plugins\font-awesome@6.7.2\webfonts文件夹中

字体文件:fa-solid-900.woff2fa-brands-400.woff2

最后一键三连即可。