mirror of
https://github.com/DIYgod/RSSHub.git
synced 2025-11-27 07:37:47 +08:00
215 lines
7.8 KiB
JavaScript
215 lines
7.8 KiB
JavaScript
/**
|
|
* ESLint 9 plugin to automatically mark NSFW routes with the nsfw flag
|
|
*/
|
|
|
|
const nsfwRoutes = [
|
|
'141jav',
|
|
'141ppv',
|
|
'18comic',
|
|
'2048',
|
|
'7mmtv',
|
|
'8kcos',
|
|
'91porn',
|
|
'95mm',
|
|
'abskoop',
|
|
'asiantolick',
|
|
'asmr-200',
|
|
'booru',
|
|
'chikubi',
|
|
'chub',
|
|
'civitai',
|
|
'cool18',
|
|
'coomer',
|
|
'copymanga',
|
|
'cosplaytele',
|
|
'dlsite',
|
|
'e-hentai',
|
|
'ehentai',
|
|
'everia',
|
|
'fanbox',
|
|
'fansly',
|
|
'fantia',
|
|
'freexcomic',
|
|
'furaffinity',
|
|
'gelbooru',
|
|
'hanime1',
|
|
'iwara',
|
|
'javbus',
|
|
'javdb',
|
|
'javlibrary',
|
|
'javtiful',
|
|
'javtrailers',
|
|
'jpxgmn',
|
|
'kemono',
|
|
'kisskiss',
|
|
'komiic',
|
|
'konachan',
|
|
'koyso',
|
|
'laimanhua',
|
|
'literotica',
|
|
'mangadex',
|
|
'manhuagui',
|
|
'manyvids',
|
|
'missav',
|
|
'netflav',
|
|
'nhentai',
|
|
'olevod',
|
|
'oreno3d',
|
|
'patreon',
|
|
'pixiv',
|
|
'pornhub',
|
|
'rawkuma',
|
|
'sehuatang',
|
|
'shuiguopai',
|
|
'sis001',
|
|
'skeb',
|
|
'skebetter',
|
|
'spankbang',
|
|
't66y',
|
|
'uraaka-joshi',
|
|
'wnacg',
|
|
'xbookcn',
|
|
'xmanhua',
|
|
'xsijishe',
|
|
'yande',
|
|
'zaimanhua',
|
|
'zodgame',
|
|
'4kup',
|
|
'misskon',
|
|
'4khd',
|
|
];
|
|
|
|
// 检查是否是 NSFW 路由文件
|
|
function isNsfwRoute(filePath) {
|
|
const normalizedPath = filePath.replaceAll('\\', '/');
|
|
return nsfwRoutes.some((nsfwKey) => {
|
|
const routePattern = `/lib/routes/${nsfwKey}/`;
|
|
return normalizedPath.includes(routePattern);
|
|
});
|
|
}
|
|
|
|
export default {
|
|
meta: {
|
|
name: '@rsshub/nsfw-flag',
|
|
version: '1.0.0',
|
|
},
|
|
configs: {
|
|
recommended: {
|
|
plugins: {
|
|
'@rsshub/nsfw-flag': 'self',
|
|
},
|
|
rules: {
|
|
'@rsshub/nsfw-flag/add-nsfw-flag': 'error',
|
|
},
|
|
},
|
|
},
|
|
rules: {
|
|
'add-nsfw-flag': {
|
|
meta: {
|
|
type: 'problem',
|
|
docs: {
|
|
description: 'Automatically add nsfw flag to NSFW routes',
|
|
category: 'Best Practices',
|
|
recommended: true,
|
|
},
|
|
fixable: 'code',
|
|
schema: [],
|
|
messages: {
|
|
missingNsfwFlag: 'NSFW route is missing the nsfw flag in features',
|
|
},
|
|
},
|
|
create(context) {
|
|
const filename = context.filename || context.getFilename();
|
|
|
|
// 如果不是 NSFW 路由,跳过检查
|
|
if (!isNsfwRoute(filename)) {
|
|
return {};
|
|
}
|
|
|
|
return {
|
|
ExportNamedDeclaration(node) {
|
|
// 查找 export const route: Route = {...}
|
|
if (
|
|
node.declaration &&
|
|
node.declaration.type === 'VariableDeclaration' &&
|
|
node.declaration.declarations &&
|
|
node.declaration.declarations[0] &&
|
|
node.declaration.declarations[0].id &&
|
|
node.declaration.declarations[0].id.name === 'route'
|
|
) {
|
|
const routeDeclaration = node.declaration.declarations[0];
|
|
const routeObject = routeDeclaration.init;
|
|
|
|
if (routeObject && routeObject.type === 'ObjectExpression') {
|
|
let featuresProperty = null;
|
|
let nsfwProperty = null;
|
|
|
|
// 查找 features 属性
|
|
for (const prop of routeObject.properties) {
|
|
if (prop.type === 'Property' && prop.key && prop.key.name === 'features') {
|
|
featuresProperty = prop;
|
|
|
|
// 在 features 中查找 nsfw 属性
|
|
if (prop.value && prop.value.type === 'ObjectExpression') {
|
|
for (const featureProp of prop.value.properties) {
|
|
if (featureProp.type === 'Property' && featureProp.key && featureProp.key.name === 'nsfw') {
|
|
nsfwProperty = featureProp;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 检查是否需要添加或修复 nsfw 标志
|
|
if (!featuresProperty) {
|
|
// 没有 features 属性,需要添加整个 features 对象
|
|
context.report({
|
|
node: routeObject,
|
|
messageId: 'missingNsfwFlag',
|
|
fix(fixer) {
|
|
// 在对象的最后添加 features 属性
|
|
const lastProperty = routeObject.properties.at(-1);
|
|
|
|
return lastProperty
|
|
? fixer.insertTextAfter(lastProperty, ',\n features: {\n nsfw: true,\n }')
|
|
: // 空对象的情况
|
|
fixer.insertTextAfter(routeObject.properties.length > 0 ? routeObject.properties.at(-1) : routeObject, '\n features: {\n nsfw: true,\n }\n');
|
|
},
|
|
});
|
|
} else if (!nsfwProperty) {
|
|
// 有 features 属性但没有 nsfw 属性
|
|
context.report({
|
|
node: featuresProperty.value,
|
|
messageId: 'missingNsfwFlag',
|
|
fix(fixer) {
|
|
const featuresObject = featuresProperty.value;
|
|
if (featuresObject.properties.length > 0) {
|
|
const lastFeatureProp = featuresObject.properties.at(-1);
|
|
return fixer.insertTextAfter(lastFeatureProp, ',\n nsfw: true');
|
|
} else {
|
|
// features 是空对象
|
|
return fixer.replaceTextRange([featuresObject.range[0] + 1, featuresObject.range[1] - 1], '\n nsfw: true,\n ');
|
|
}
|
|
},
|
|
});
|
|
} else if (nsfwProperty.value && (nsfwProperty.value.type !== 'Literal' || nsfwProperty.value.value !== true)) {
|
|
// nsfw 属性存在但不是 true
|
|
context.report({
|
|
node: nsfwProperty.value,
|
|
messageId: 'missingNsfwFlag',
|
|
fix(fixer) {
|
|
return fixer.replaceText(nsfwProperty.value, 'true');
|
|
},
|
|
});
|
|
}
|
|
}
|
|
}
|
|
},
|
|
};
|
|
},
|
|
},
|
|
},
|
|
};
|