Update sandbox (#858)

* add htmlparser2

* update Hako

* find chapter text with regex

* flat json

* use native language

* fix sorting

* v2.1.0
This commit is contained in:
nyagami
2024-03-26 17:41:22 +07:00
committed by GitHub
parent e09c58c5f5
commit 113c5a1f97
5 changed files with 339 additions and 188 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
# LNReader Plugins # LNReader Plugins
<p> <p>
<img alt="LNReader plugins counting" src="https://raw.githubusercontent.com/LNReader/lnreader-sources/plugins/v2.0.0/total.svg"> <img alt="LNReader plugins counting" src="https://raw.githubusercontent.com/LNReader/lnreader-sources/plugins/v2.1.0/total.svg">
<img alt="GitHub issues by-label" src="https://img.shields.io/github/issues/lnreader/lnreader-sources/Source%20Request?color=success&label=source%20requests"> <img alt="GitHub issues by-label" src="https://img.shields.io/github/issues/lnreader/lnreader-sources/Source%20Request?color=success&label=source%20requests">
<img alt="GitHub issues by-label" src="https://img.shields.io/github/issues/lnreader/lnreader-sources/Bug?color=red&label=bugs"> <img alt="GitHub issues by-label" src="https://img.shields.io/github/issues/lnreader/lnreader-sources/Bug?color=red&label=bugs">
</p> </p>
+75 -12
View File
@@ -1,12 +1,12 @@
{ {
"name": "lnreader-plugins", "name": "lnreader-plugins",
"version": "2.0.0", "version": "2.1.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "lnreader-plugins", "name": "lnreader-plugins",
"version": "2.0.0", "version": "2.1.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"body-parser": "^1.20.2", "body-parser": "^1.20.2",
@@ -15,6 +15,7 @@
"cli-color": "^2.0.3", "cli-color": "^2.0.3",
"dayjs": "^1.11.10", "dayjs": "^1.11.10",
"express": "^4.18.2", "express": "^4.18.2",
"htmlparser2": "^9.1.0",
"less": "^4.2.0", "less": "^4.2.0",
"module-alias": "^2.2.3", "module-alias": "^2.2.3",
"protobufjs": "^7.2.6", "protobufjs": "^7.2.6",
@@ -870,6 +871,24 @@
"url": "https://github.com/sponsors/fb55" "url": "https://github.com/sponsors/fb55"
} }
}, },
"node_modules/cheerio/node_modules/htmlparser2": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
"funding": [
"https://github.com/fb55/htmlparser2?sponsor=1",
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
],
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3",
"domutils": "^3.0.1",
"entities": "^4.4.0"
}
},
"node_modules/chokidar": { "node_modules/chokidar": {
"version": "3.5.3", "version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
@@ -2110,9 +2129,9 @@
} }
}, },
"node_modules/htmlparser2": { "node_modules/htmlparser2": {
"version": "8.0.2", "version": "9.1.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz",
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==",
"funding": [ "funding": [
"https://github.com/fb55/htmlparser2?sponsor=1", "https://github.com/fb55/htmlparser2?sponsor=1",
{ {
@@ -2123,8 +2142,8 @@
"dependencies": { "dependencies": {
"domelementtype": "^2.3.0", "domelementtype": "^2.3.0",
"domhandler": "^5.0.3", "domhandler": "^5.0.3",
"domutils": "^3.0.1", "domutils": "^3.1.0",
"entities": "^4.4.0" "entities": "^4.5.0"
} }
}, },
"node_modules/http-errors": { "node_modules/http-errors": {
@@ -3661,6 +3680,24 @@
"postcss": "^8.3.11" "postcss": "^8.3.11"
} }
}, },
"node_modules/sanitize-html/node_modules/htmlparser2": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
"funding": [
"https://github.com/fb55/htmlparser2?sponsor=1",
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
],
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3",
"domutils": "^3.0.1",
"entities": "^4.4.0"
}
},
"node_modules/sax": { "node_modules/sax": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz",
@@ -5185,6 +5222,19 @@
"htmlparser2": "^8.0.1", "htmlparser2": "^8.0.1",
"parse5": "^7.0.0", "parse5": "^7.0.0",
"parse5-htmlparser2-tree-adapter": "^7.0.0" "parse5-htmlparser2-tree-adapter": "^7.0.0"
},
"dependencies": {
"htmlparser2": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
"requires": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3",
"domutils": "^3.0.1",
"entities": "^4.4.0"
}
}
} }
}, },
"cheerio-select": { "cheerio-select": {
@@ -6182,14 +6232,14 @@
"integrity": "sha512-eVcrzgbR4tim7c7soKQKtxa/kQM4TzjnlU83rcZ9bHU6t31ehfV7SktN6McWgwPWg+JYMA/O3qpGxBvFq1z2Jg==" "integrity": "sha512-eVcrzgbR4tim7c7soKQKtxa/kQM4TzjnlU83rcZ9bHU6t31ehfV7SktN6McWgwPWg+JYMA/O3qpGxBvFq1z2Jg=="
}, },
"htmlparser2": { "htmlparser2": {
"version": "8.0.2", "version": "9.1.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz",
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==",
"requires": { "requires": {
"domelementtype": "^2.3.0", "domelementtype": "^2.3.0",
"domhandler": "^5.0.3", "domhandler": "^5.0.3",
"domutils": "^3.0.1", "domutils": "^3.1.0",
"entities": "^4.4.0" "entities": "^4.5.0"
} }
}, },
"http-errors": { "http-errors": {
@@ -7290,6 +7340,19 @@
"is-plain-object": "^5.0.0", "is-plain-object": "^5.0.0",
"parse-srcset": "^1.0.2", "parse-srcset": "^1.0.2",
"postcss": "^8.3.11" "postcss": "^8.3.11"
},
"dependencies": {
"htmlparser2": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
"requires": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3",
"domutils": "^3.0.1",
"entities": "^4.4.0"
}
}
} }
}, },
"sax": { "sax": {
+2 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "lnreader-plugins", "name": "lnreader-plugins",
"version": "2.0.0", "version": "2.1.0",
"description": "Plugins repo for LNReader", "description": "Plugins repo for LNReader",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
@@ -24,6 +24,7 @@
"cli-color": "^2.0.3", "cli-color": "^2.0.3",
"dayjs": "^1.11.10", "dayjs": "^1.11.10",
"express": "^4.18.2", "express": "^4.18.2",
"htmlparser2": "^9.1.0",
"less": "^4.2.0", "less": "^4.2.0",
"module-alias": "^2.2.3", "module-alias": "^2.2.3",
"protobufjs": "^7.2.6", "protobufjs": "^7.2.6",
+242 -157
View File
@@ -1,7 +1,6 @@
import { CheerioAPI, load as parseHTML } from 'cheerio'; import { Parser } from 'htmlparser2';
import { fetchFile } from '@libs/fetch'; import { fetchFile } from '@libs/fetch';
import { Plugin } from '@typings/plugin'; import { Plugin } from '@typings/plugin';
import { isUrlAbsolute } from '@libs/isAbsoluteUrl';
import { NovelStatus } from '@libs/novelStatus'; import { NovelStatus } from '@libs/novelStatus';
import { FilterTypes, Filters } from '@libs/filterInputs'; import { FilterTypes, Filters } from '@libs/filterInputs';
@@ -11,29 +10,43 @@ class HakoPlugin implements Plugin.PluginBase {
icon = 'src/vi/hakolightnovel/icon.png'; icon = 'src/vi/hakolightnovel/icon.png';
site = 'https://ln.hako.vn'; site = 'https://ln.hako.vn';
version = '1.0.1'; version = '1.0.1';
parseNovels(loadedCheerio: CheerioAPI) { parseNovels(url: string) {
const novels: Plugin.NovelItem[] = []; return fetch(url)
loadedCheerio('#mainpart .row .thumb-item-flow').each((index, ele) => { .then(res => res.text())
let url = loadedCheerio(ele) .then(html => {
.find('div.thumb_attr.series-title > a') const novels: Plugin.NovelItem[] = [];
.attr('href'); let tempNovel = {} as Plugin.NovelItem;
let isGettingUrl = false;
if (url) { let isParsingNovel = false;
const name = loadedCheerio(ele).find('.series-title').text().trim(); const parser = new Parser({
let cover = loadedCheerio(ele).find('.img-in-ratio').attr('data-bg'); onopentag(name, attribs) {
if (attribs['class']?.includes('thumb-item-flow')) {
if (cover && !isUrlAbsolute(cover)) { isParsingNovel = true;
cover = this.site + cover; }
} if (isParsingNovel) {
if (attribs['class']?.includes('series-title')) {
const novel = { name, path: url.replace(this.site, ''), cover }; isGettingUrl = true;
}
novels.push(novel); if (attribs['class']?.includes('img-in-ratio')) {
} tempNovel.cover = attribs['data-bg'];
}); }
return novels; if (isGettingUrl && name === 'a') {
tempNovel.name = attribs['title'];
tempNovel.path = attribs['href'];
novels.push(tempNovel);
tempNovel = {} as Plugin.NovelItem; // re-assign new reference
isGettingUrl = false;
isParsingNovel = false;
}
}
},
});
parser.write(html);
parser.end();
return novels;
});
} }
async popularNovels( popularNovels(
pageNo: number, pageNo: number,
{ {
showLatestNovels, showLatestNovels,
@@ -41,156 +54,228 @@ class HakoPlugin implements Plugin.PluginBase {
}: Plugin.PopularNovelsOptions<typeof this.filters>, }: Plugin.PopularNovelsOptions<typeof this.filters>,
): Promise<Plugin.NovelItem[]> { ): Promise<Plugin.NovelItem[]> {
let link = this.site + '/danh-sach'; let link = this.site + '/danh-sach';
if (filters.alphabet.value) { if (filters) {
link += '/' + filters.alphabet.value; if (filters.alphabet.value) {
link += '/' + filters.alphabet.value;
}
const params = new URLSearchParams();
for (const novelType of filters.type.value) {
params.append(novelType, '1');
}
for (const status of filters.status.value) {
params.append(status, '1');
}
params.append('sapxep', filters.sort.value);
link += '?' + params.toString() + '&page=' + pageNo;
} else {
link += '?page=' + pageNo;
} }
const params = new URLSearchParams(); return this.parseNovels(link);
for (const novelType of filters.type.value) {
params.append(novelType, '1');
}
for (const status of filters.status.value) {
params.append(status, '1');
}
params.append('sapxep', filters.sort.value);
link += '?' + params.toString() + '&page=' + pageNo;
const result = await fetch(link);
const body = await result.text();
const loadedCheerio = parseHTML(body);
return this.parseNovels(loadedCheerio);
} }
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> { parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
const novel: Plugin.SourceNovel = { return fetch(this.site + novelPath)
path: novelPath, .then(res => res.text())
name: 'Không có tiêu đề', .then(html => {
}; const novel: Plugin.SourceNovel = {
const result = await fetch(this.site + novelPath); path: novelPath,
const body = await result.text(); name: '',
author: '',
let loadedCheerio = parseHTML(body); artist: '',
summary: '',
novel.name = loadedCheerio('.series-name').text().trim(); genres: '',
status: '',
const background = };
loadedCheerio('.series-cover > .a6-ratio > div').attr('style') || ''; let isReadingName = 0;
const novelCover = background.substring( let isReadingSummary = 0;
background.indexOf('http'), let isReadingGenre = 0;
background.length - 2, let isReadingInfo = 0;
); let isReadingInfoValue = 0;
let isParsingVolume = false;
novel.cover = novelCover let isReadingVolume = false;
? isUrlAbsolute(novelCover) let currentVolume = '';
? novelCover let isParsingChapterList = false;
: this.site + novelCover let num = 0,
: ''; part = 0;
let isReadingChapterTime = false;
novel.summary = loadedCheerio('.summary-content').text().trim(); const chapters: Plugin.ChapterItem[] = [];
let tempChapter = {} as Plugin.ChapterItem;
novel.genres = loadedCheerio('.series-information > div:nth-child(1)') const parser = new Parser({
.text() onopentag(name, attribs) {
.trim() if (attribs['class'] === 'series-name') {
.split(/\n[\s\n]*/) isReadingName = 1;
.join(','); } else if (
!novel.cover &&
novel.author = loadedCheerio( attribs['class']?.includes('img-in-ratio')
'.series-information > div:nth-child(2) > .info-value', ) {
) const background = attribs['style'];
.text() if (background) {
.trim(); novel.cover = background.substring(
novel.artist = loadedCheerio( background.indexOf('http'),
'.series-information > div:nth-child(3) > .info-value', background.length - 2,
) );
.text() }
.trim(); } else if (attribs['class'] === 'summary-content') {
isReadingSummary = 1;
novel.status = loadedCheerio( } else if (attribs['class'] === 'series-gerne-item') {
'.series-information > div:nth-child(4) > .info-value', isReadingGenre = 1;
) } else if (attribs['class'] === 'info-item') {
.text() isReadingInfo += 1;
.trim(); } else if (attribs['class']?.includes('volume-list')) {
isParsingVolume = true;
switch (novel.status) { num = 0;
case 'Đang tiến hành': part = 1;
novel.status = NovelStatus.Ongoing; } else if (isParsingVolume && attribs['class'] === 'sect-title') {
break; isReadingVolume = true;
case 'Tạm ngưng': currentVolume = '';
novel.status = NovelStatus.OnHiatus; } else if (isParsingVolume && name === 'ul') {
break; isParsingChapterList = true;
case 'Completed': } else if (
novel.status = NovelStatus.Completed; isParsingChapterList &&
break; name === 'a' &&
default: attribs['title'] !== null
novel.status = NovelStatus.Unknown; ) {
} const chapterName = attribs['title'];
let chapterNumber = Number(
novel.chapters = []; chapterName.match(/Chương\s*(\d+)/i)?.[1],
loadedCheerio('.volume-list').each((idx, ele) => { );
const customPage = loadedCheerio(ele).find('.sect-title').text().trim(); if (chapterNumber) {
let num = 0, if (num === chapterNumber) {
part = 1; chapterNumber = num + part / 10;
loadedCheerio(ele) part += 1;
.find('.list-chapters > li') } else {
.each((idx, chapterEle) => { num = chapterNumber;
const path = loadedCheerio(chapterEle).find('a').attr('href'); part = 1;
const chapterName = loadedCheerio(chapterEle) }
.find('.chapter-name') } else {
.text() chapterNumber = num + part / 10;
.trim(); part++;
let chapterNumber = Number(chapterName.match(/Chương\s*(\d+)/i)?.[1]); }
if (chapterNumber) { tempChapter = {
num = chapterNumber; path: attribs['href'],
part = 1; name: chapterName,
} else { page: currentVolume,
chapterNumber = num + part / 10; chapterNumber: chapterNumber,
part++; };
} } else if (
const chapterTime = loadedCheerio(chapterEle) isParsingChapterList &&
.find('.chapter-time') attribs['class'] === 'chapter-time'
.text() ) {
.split('/') isReadingChapterTime = true;
.map(x => Number(x)); }
if (path) { if (isReadingName === 1 && name === 'a') {
novel.chapters?.push({ isReadingName = 2;
page: customPage, }
path, if (isReadingInfo !== 0 && name === 'a') {
name: chapterName, isReadingInfoValue = 1;
releaseTime: new Date( }
},
ontext(data) {
if (isReadingName === 2) {
novel.name += data;
} else if (isReadingSummary !== 0) {
if (isReadingSummary === 2) {
novel.summary += '\n' + data;
isReadingSummary = 1;
} else {
novel.summary += data;
}
} else if (isReadingGenre === 1) {
novel.genres += data;
} else if (isReadingInfoValue === 1 && isReadingInfo <= 3) {
if (isReadingInfo === 1) {
novel.author += data;
} else if (isReadingInfo === 2) {
novel.artist += data;
} else if (isReadingInfo === 3) {
novel.status += data;
}
} else if (isReadingVolume) {
currentVolume += data.trim();
} else if (isReadingChapterTime) {
const chapterTime = data.split('/').map(x => Number(x));
tempChapter.releaseTime = new Date(
chapterTime[2], chapterTime[2],
chapterTime[1], chapterTime[1],
chapterTime[0], chapterTime[0],
).toISOString(), ).toISOString();
chapterNumber: chapterNumber, chapters.push(tempChapter);
}); isReadingChapterTime = false;
} tempChapter = {} as Plugin.ChapterItem;
}
},
onclosetag(name) {
if (isReadingName === 2) {
isReadingName = 0;
} else if (isReadingSummary !== 0) {
if (name !== 'div') {
isReadingSummary = 2;
} else {
isReadingSummary = 0;
}
} else if (isReadingGenre === 1) {
isReadingGenre = 0;
novel.genres += ',';
} else if (isReadingVolume) {
isReadingVolume = false;
} else if (isReadingChapterTime) isReadingChapterTime = false;
if (name === 'ul') {
isParsingChapterList = false;
}
if (isReadingInfoValue === 1) {
isReadingInfoValue = 0;
if (isReadingInfo === 3) {
isReadingInfo = 0;
}
}
if (name === 'section') {
isParsingVolume = false;
}
},
}); });
});
return novel; parser.write(html);
parser.end();
novel.chapters = chapters;
switch (novel.status?.trim()) {
case 'Đang tiến hành':
novel.status = NovelStatus.Ongoing;
break;
case 'Tạm ngưng':
novel.status = NovelStatus.OnHiatus;
break;
case 'Completed':
novel.status = NovelStatus.Completed;
break;
default:
novel.status = NovelStatus.Unknown;
}
novel.genres = novel.genres?.replace(/,*\s*$/, '');
return novel;
});
} }
async parseChapter(chapterPath: string): Promise<string> { parseChapter(chapterPath: string): Promise<string> {
const result = await fetch(this.site + chapterPath); return fetch(this.site + chapterPath)
const body = await result.text(); .then(res => res.text())
.then(
const loadedCheerio = parseHTML(body); html =>
html.match(
const chapterText = loadedCheerio('#chapter-content').html() || ''; /(<div id="chapter-content".+?>[^]+)<div style="text-align: center;/,
)?.[1] || 'Không tìm thấy nội dung',
return chapterText; );
} }
async searchNovels( searchNovels(
searchTerm: string, searchTerm: string,
pageNo: number, pageNo: number,
): Promise<Plugin.NovelItem[]> { ): Promise<Plugin.NovelItem[]> {
const url = const url =
this.site + '/tim-kiem?keywords=' + searchTerm + '&page=' + pageNo; this.site + '/tim-kiem?keywords=' + searchTerm + '&page=' + pageNo;
const result = await fetch(url); return this.parseNovels(url);
const body = await result.text();
const loadedCheerio = parseHTML(body);
return this.parseNovels(loadedCheerio);
} }
async fetchImage(url: string): Promise<string | undefined> { fetchImage(url: string): Promise<string | undefined> {
const headers = { const headers = {
Referer: 'https://ln.hako.vn', Referer: 'https://ln.hako.vn',
}; };
return await fetchFile(url, { headers: headers }); return fetchFile(url, { headers: headers });
} }
filters = { filters = {
alphabet: { alphabet: {
+19 -17
View File
@@ -24,17 +24,7 @@ const USER_CONTENT_LINK = `https://raw.githubusercontent.com/${USERNAME}/${REPO}
const ICON_LINK = `${USER_CONTENT_LINK}/icons`; const ICON_LINK = `${USER_CONTENT_LINK}/icons`;
const PLUGIN_LINK = `${USER_CONTENT_LINK}/.js/plugins`; const PLUGIN_LINK = `${USER_CONTENT_LINK}/.js/plugins`;
const json: { const json: HostedPluginItem[] = [];
[key: string]: {
id: string;
name: string;
site: string;
lang: string;
version: string;
url: string;
iconUrl: string;
}[];
} = {};
if (!fs.existsSync(path.join(outRoot, '.dist'))) { if (!fs.existsSync(path.join(outRoot, '.dist'))) {
fs.mkdirSync(path.join(outRoot, '.dist')); fs.mkdirSync(path.join(outRoot, '.dist'));
} }
@@ -43,12 +33,21 @@ const jsonMinPath = path.join(outRoot, '.dist', 'plugins.min.json');
const pluginSet = new Set(); const pluginSet = new Set();
let totalPlugins = 0; let totalPlugins = 0;
interface HostedPluginItem {
id: string;
name: string;
site: string;
lang: string;
version: string;
url: string;
iconUrl: string;
}
for (let language in languages) { for (let language in languages) {
// language with English name // language with English name
const langPath = path.join(root, 'plugins', language.toLowerCase()); const langPath = path.join(root, 'plugins', language.toLowerCase());
if (!fs.existsSync(langPath)) continue; if (!fs.existsSync(langPath)) continue;
const plugins = fs.readdirSync(langPath); const plugins = fs.readdirSync(langPath);
json[language] = [];
plugins.forEach(plugin => { plugins.forEach(plugin => {
if (plugin.startsWith('.')) return; if (plugin.startsWith('.')) return;
minify(path.join(langPath, plugin)); minify(path.join(langPath, plugin));
@@ -59,15 +58,15 @@ for (let language in languages) {
const { id, name, site, version, icon } = instance; const { id, name, site, version, icon } = instance;
const normalisedName = name.replace(/\[.*\]/, ''); const normalisedName = name.replace(/\[.*\]/, '');
const info = { const info: HostedPluginItem = {
id, id,
name: normalisedName, name: normalisedName,
site, site,
lang: language, lang: languages[language as keyof typeof languages],
version, version,
url: `${PLUGIN_LINK}/${language.toLowerCase()}/${plugin}`, url: `${PLUGIN_LINK}/${language.toLowerCase()}/${plugin}`,
iconUrl: `${ICON_LINK}/${icon}`, iconUrl: `${ICON_LINK}/${icon}`,
} as const; };
if (pluginSet.has(id)) { if (pluginSet.has(id)) {
console.log("There's already a plugin with id:", id); console.log("There's already a plugin with id:", id);
@@ -75,13 +74,16 @@ for (let language in languages) {
} else { } else {
pluginSet.add(id); pluginSet.add(id);
} }
json[language].push(info); json.push(info);
totalPlugins += 1; totalPlugins += 1;
console.log(name, '✅'); console.log(name, '✅');
}); });
} }
for (let lang in json) json[lang].sort((a, b) => a.id.localeCompare(b.id)); json.sort((a, b) => {
if (a.lang === b.lang) return a.id.localeCompare(b.id);
return 0;
});
fs.writeFileSync(jsonMinPath, JSON.stringify(json)); fs.writeFileSync(jsonMinPath, JSON.stringify(json));
fs.writeFileSync(jsonPath, JSON.stringify(json, null, '\t')); fs.writeFileSync(jsonPath, JSON.stringify(json, null, '\t'));