前言
solitude主题的配置项可以自定义外部调用的地址,但是一直用别人的加速,就怕有一天出现问题导致还需要重新替换
所以打算弄成本地话,本教程基于在 Solitude 上优雅的使用 CDN 加速静态资源文件修改
教程
本教程适用的版本为v3.0.20,若有不同,请自行修改脚本代码
首先就是在根目录创建一个名为plugins.js的文件,然后将如下代码放进去,其中的isNPM和foderName根据实际情况修改
#!/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.woff2和fa-brands-400.woff2
最后一键三连即可。