diff --git a/app/electron/eslint.config.js b/app/electron/eslint.config.js index 4901421978..7d2155f1a5 100644 --- a/app/electron/eslint.config.js +++ b/app/electron/eslint.config.js @@ -1,4 +1,5 @@ import tseslint from 'typescript-eslint'; +import stylistic from '@stylistic/eslint-plugin'; export default tseslint.config({ files: [ @@ -7,6 +8,7 @@ export default tseslint.config({ plugins: { '@typescript-eslint': tseslint.plugin, //'eslint-plugin-tsdoc': ..., + '@stylistic': stylistic, }, languageOptions: { ecmaVersion: 2018, @@ -17,7 +19,7 @@ export default tseslint.config({ rules: { 'semi': [ 'error', 'always' ], 'no-extra-parens': 'off', - '@typescript-eslint/no-extra-parens': 'error', + '@stylistic/no-extra-parens': 'error', '@typescript-eslint/no-unused-vars': [ 'error', { 'argsIgnorePattern': '^_' } ], '@typescript-eslint/consistent-type-imports': 'error', '@typescript-eslint/ban-ts-comment': 'warn', diff --git a/app/electron/package.json b/app/electron/package.json index 16424a7c18..473adb9047 100644 --- a/app/electron/package.json +++ b/app/electron/package.json @@ -14,9 +14,9 @@ "yargs": "17.7.2" }, "devDependencies": { - "@types/ws": "8.5.11", + "@types/ws": "8.5.12", "extract-zip": "2.0.1", - "electron": "31.3.0", + "electron": "31.3.1", "plist": "3.1.0" }, "scripts": { diff --git a/app/nw/eslint.config.js b/app/nw/eslint.config.js index 4901421978..7d2155f1a5 100644 --- a/app/nw/eslint.config.js +++ b/app/nw/eslint.config.js @@ -1,4 +1,5 @@ import tseslint from 'typescript-eslint'; +import stylistic from '@stylistic/eslint-plugin'; export default tseslint.config({ files: [ @@ -7,6 +8,7 @@ export default tseslint.config({ plugins: { '@typescript-eslint': tseslint.plugin, //'eslint-plugin-tsdoc': ..., + '@stylistic': stylistic, }, languageOptions: { ecmaVersion: 2018, @@ -17,7 +19,7 @@ export default tseslint.config({ rules: { 'semi': [ 'error', 'always' ], 'no-extra-parens': 'off', - '@typescript-eslint/no-extra-parens': 'error', + '@stylistic/no-extra-parens': 'error', '@typescript-eslint/no-unused-vars': [ 'error', { 'argsIgnorePattern': '^_' } ], '@typescript-eslint/consistent-type-imports': 'error', '@typescript-eslint/ban-ts-comment': 'warn', diff --git a/app/nw/package.json b/app/nw/package.json index 4263546710..065a96231f 100755 --- a/app/nw/package.json +++ b/app/nw/package.json @@ -14,9 +14,9 @@ "yargs": "17.7.2" }, "devDependencies": { - "@types/ws": "8.5.11", + "@types/ws": "8.5.12", "extract-zip": "2.0.1", - "nw": "0.89.0-sdk", + "nw": "0.90.0-sdk", "plist": "3.1.0" }, "scripts": { diff --git a/package.json b/package.json index f1e84ca232..700262b230 100755 --- a/package.json +++ b/package.json @@ -12,18 +12,19 @@ "docs" ], "devDependencies": { + "@stylistic/eslint-plugin": "^2.6.1", "@types/chrome": "^0.0.269", "@types/jsdom": "^21.1.7", "@types/nw.js": "^0.13.21", "eslint": "^9.8.0", "eslint-plugin-tsdoc": "^0.3.0", "jsdom": "^24.1.1", - "puppeteer-core": "^22.14.0", + "puppeteer-core": "^22.15.0", "tslib": "^2.6.3", "typescript": "^5.5.4", - "typescript-eslint": "^7.17.0", + "typescript-eslint": "^8.0.0", "vite": "^5.3.5", - "vitest": "^2.0.4", + "vitest": "^2.0.5", "vitest-mock-extended": "^2.0.0" }, "scripts": { diff --git a/web/eslint.config.js b/web/eslint.config.js index 9a1493a567..270293aedf 100644 --- a/web/eslint.config.js +++ b/web/eslint.config.js @@ -1,4 +1,5 @@ import tseslint from 'typescript-eslint'; +import stylistic from '@stylistic/eslint-plugin'; export default tseslint.config({ files: [ @@ -9,6 +10,7 @@ export default tseslint.config({ plugins: { '@typescript-eslint': tseslint.plugin, //'eslint-plugin-tsdoc': ..., + '@stylistic': stylistic, }, languageOptions: { ecmaVersion: 2018, @@ -19,7 +21,7 @@ export default tseslint.config({ rules: { 'semi': [ 'error', 'always' ], 'no-extra-parens': 'off', - '@typescript-eslint/no-extra-parens': 'error', + '@stylistic/no-extra-parens': 'error', '@typescript-eslint/no-unused-vars': [ 'error', { 'argsIgnorePattern': '^_' } ], '@typescript-eslint/consistent-type-imports': 'error', '@typescript-eslint/ban-ts-comment': 'warn', diff --git a/web/package.json b/web/package.json index 3f8d47a5ad..10854cbdf8 100755 --- a/web/package.json +++ b/web/package.json @@ -11,7 +11,7 @@ "protobufjs": "^7.3.2" }, "devDependencies": { - "@fluentui/svg-icons": "^1.1.250", + "@fluentui/svg-icons": "^1.1.252", "@fluentui/web-components": "^2.6.1", "@microsoft/fast-element": "^1.13.0", "@svelte-put/dragscroll": "^3.0.1", @@ -22,7 +22,7 @@ "@types/react-dom": "^18.3.0", "@types/wicg-file-system-access": "^2023.10.5", "@vitejs/plugin-react": "^4.3.1", - "@vitejs/plugin-vue": "^5.1.1", + "@vitejs/plugin-vue": "^5.1.2", "@vscode/codicons": "^0.0.36", "carbon-components-svelte": "^0.85.0", "carbon-icons-svelte": "^12.10.0", @@ -31,9 +31,9 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "svelte": "^4.2.18", - "svelte-check": "^3.8.4", + "svelte-check": "^3.8.5", "svelte-preprocess": "^6.0.2", - "vue": "^3.4.34", + "vue": "^3.4.35", "vue-tsc": "^2.0.29" }, "scripts": { diff --git a/web/src/engine/platform/electron/FetchProvider.ts b/web/src/engine/platform/electron/FetchProvider.ts index d33790153c..e948bdb695 100644 --- a/web/src/engine/platform/electron/FetchProvider.ts +++ b/web/src/engine/platform/electron/FetchProvider.ts @@ -130,7 +130,7 @@ export default class extends FetchProvider { await destroy(); resolve(result); } - } catch(error) { + } catch { await destroy(); } }); diff --git a/web/src/engine/websites/Alphapolis.ts b/web/src/engine/websites/Alphapolis.ts index b6dafea23b..6ff0c3b449 100644 --- a/web/src/engine/websites/Alphapolis.ts +++ b/web/src/engine/websites/Alphapolis.ts @@ -8,15 +8,12 @@ import { Exception } from '../Error'; import { WebsiteResourceKey as R } from '../../i18n/ILocale'; import { AddAntiScrapingDetection, FetchRedirection } from '../platform/AntiScrapingDetection'; -type JSONPages = [JSONObject | string] - AddAntiScrapingDetection(async (render) => { const dom = await render(); return dom.documentElement.innerHTML.includes('window.awsWafCookieDomainList') ? FetchRedirection.Automatic : undefined; }); function ChaptersExtractor(element: HTMLElement) { - const id = element instanceof HTMLAnchorElement ? element.pathname : element.querySelector('a.read-episode').pathname; const title = element.querySelector('.title').textContent.trim(); return { id, title }; @@ -48,13 +45,17 @@ export default class extends DecoratableMangaScraper { let viewer: HTMLElement = undefined; try { [viewer] = await FetchCSS(new Request(new URL(chapter.Identifier, this.URI)), '[v-bind\\:pages]'); - } catch (error) { + } catch { // TODO: Do not use same message for generic errors throw new Exception(R.Plugin_Common_Chapter_UnavailableError); } - const pages: JSONPages = JSON.parse(viewer.getAttribute('v-bind:pages')); + const links: unknown[] = JSON.parse(viewer.getAttribute('v-bind:pages')); const isVertical = viewer.getAttribute('v-bind:is-vertical-manga') === '1'; - return pages.filter(element => typeof element === 'string' && !element.match('white_page') && element != '').map(element => new Page(this, chapter, isVertical ? new URL(element as string) : new URL((element as string).replace(/\/[0-9]+x[0-9]+/, '/1080x1536')), isVertical ? undefined : { fallbackURL: element })); - + return (links + .filter(link => link && typeof link === 'string' && !/white_page/.test(link)) as string[]) + .map(link => { + const uri = new URL(isVertical ? link : link.replace(/\/[0-9]+x[0-9]+/, '/1080x1536')); + return new Page(this, chapter, uri, isVertical ? null : { fallbackURL: link }); + }); } //Since high resolution is not always available, use the real picture url instead of the forces one in case of failure diff --git a/web/src/engine/websites/ComicFuz.ts b/web/src/engine/websites/ComicFuz.ts index 897c7b2f99..6e6e8d9e0d 100644 --- a/web/src/engine/websites/ComicFuz.ts +++ b/web/src/engine/websites/ComicFuz.ts @@ -132,7 +132,7 @@ export default class extends DecoratableMangaScraper { let data: MangaViewerResponse = undefined; try { data = await FetchProto(request, protoTypes, 'ComicFuz.MangaViewerResponse'); - } catch (error) { + } catch { // TODO: Do not use same message for generic errors throw new Exception(R.Plugin_Common_Chapter_UnavailableError); } return data.pages diff --git a/web/src/engine/websites/ComicK.ts b/web/src/engine/websites/ComicK.ts index d4ff92737f..67db71127b 100644 --- a/web/src/engine/websites/ComicK.ts +++ b/web/src/engine/websites/ComicK.ts @@ -90,7 +90,7 @@ export default class extends DecoratableMangaScraper { try { const data = await FetchJSON(new Request(new URL(`v1.0/search?page=${page}&limit=49`, this.apiUrl))); return data.map(item => new Manga(this, provider, item.hid, item.title.trim())); - } catch (error) { + } catch { // TODO: Do not return empty list for generic errors return []; } } @@ -136,5 +136,4 @@ export default class extends DecoratableMangaScraper { const { chapter: { md_images } } = await FetchJSON(new Request(new URL(`/chapter/${chapter.Identifier}`, this.apiUrl))); return md_images.map(image => new Page(this, chapter, new URL(image.b2key, `https://s3.comick.ink/comick/`), { Referer: this.URI.href })); } - } \ No newline at end of file diff --git a/web/src/engine/websites/CopyManga.ts b/web/src/engine/websites/CopyManga.ts index eeb836ff64..6e352731a6 100644 --- a/web/src/engine/websites/CopyManga.ts +++ b/web/src/engine/websites/CopyManga.ts @@ -49,7 +49,6 @@ export default class extends DecoratableMangaScraper { //this.Settings.url = new Text('urloverride', W.Plugin_Settings_UrlOverride, W.Plugin_Settings_UrlOverrideInfo, this.URI.href); //(this.Settings.url as Text).Subscribe(value => this.URI.href = value); //this.URI.href = this.Settings.url.Value as string; - } public override get Icon() { @@ -81,7 +80,7 @@ export default class extends DecoratableMangaScraper { const request = this.CreateApiRequest(`/api/v3/comics?ordering=-datetime_updated&limit=50&offset=${page * 50}`); const data = await FetchJSON>>(request); return data.results.list.map(item => new Manga(this, provider, item.path_word, item.name.trim())); - } catch (error) { + } catch { // TODO: Do not return empty list for generic errors return []; } } @@ -100,7 +99,6 @@ export default class extends DecoratableMangaScraper { const imageData = dataElement.getAttribute('contentKey'); const images = await this.Decrypt(imageData); return images.map(image => new Page(this, chapter, new URL(image.url))); - } private async Decrypt(encryptedData: string): Promise { diff --git a/web/src/engine/websites/DynastyScans.ts b/web/src/engine/websites/DynastyScans.ts index 89da79fc3b..2a1a8edbc4 100644 --- a/web/src/engine/websites/DynastyScans.ts +++ b/web/src/engine/websites/DynastyScans.ts @@ -57,7 +57,7 @@ export default class extends DecoratableMangaScraper { } } } - catch (error) { + catch { // TODO: Do not return incomplete list for generic errors run = false; } } diff --git a/web/src/engine/websites/MangaFire.ts b/web/src/engine/websites/MangaFire.ts index 112db45ce2..83abded016 100644 --- a/web/src/engine/websites/MangaFire.ts +++ b/web/src/engine/websites/MangaFire.ts @@ -69,17 +69,14 @@ export default class extends DecoratableMangaScraper { const id = JSON.stringify({ itemid: chapter.dataset.id, itemtype: type, language: language }); const title = chapter.text.trim(); const newChapter = new Chapter(this, manga, id, title); - try { + if(chapterLanguageMap[language]) { newChapter.Tags.push(chapterLanguageMap[language]); - } catch (error) { - //console.warn('Unable to find language') } chapterList.push(newChapter); }); } } return chapterList; - } public override async FetchPages(chapter: Chapter): Promise { @@ -134,6 +131,5 @@ export default class extends DecoratableMangaScraper { } }); - } } \ No newline at end of file diff --git a/web/src/engine/websites/MangaNexus.ts b/web/src/engine/websites/MangaNexus.ts index e567c6838e..034b5d382e 100644 --- a/web/src/engine/websites/MangaNexus.ts +++ b/web/src/engine/websites/MangaNexus.ts @@ -88,12 +88,11 @@ export default class extends DecoratableMangaScraper { try { const url = new URL(`/_next/data/${this.nextBuild}/lista-de-mangas.json?p=${page}`, this.URI).href; const request = new Request(url); - const data = await FetchJSON(request); - return data.pageProps.mangas.items.map(element => new Manga(this, provider, element.slug, element.name.trim())); - } catch (error) { + const { pageProps: { mangas: { items } } } = await FetchJSON(request); + return items.map(item => new Manga(this, provider, item.slug, item.name.trim())); + } catch { // TODO: Do not return empty list for generic errors return []; } - } public override async FetchChapters(manga: Manga): Promise { diff --git a/web/src/engine/websites/ShonenMagazine.ts b/web/src/engine/websites/ShonenMagazine.ts index b38974856a..0b12946236 100644 --- a/web/src/engine/websites/ShonenMagazine.ts +++ b/web/src/engine/websites/ShonenMagazine.ts @@ -14,23 +14,23 @@ export default class extends DecoratableMangaScraper { //Shonenmagazine.com && pocket.shonenmagazine.com mangas url starts with https://pocket.shonenmagazine.com so this plugin handles both. super('shonenmagazine', `週刊少年マガジ (Weekly Shonen Magazine & Pocket Magazine)`, 'https://pocket.shonenmagazine.com', Tags.Language.Japanese, Tags.Source.Official, Tags.Media.Manga); } + public override get Icon() { return icon; } public override async FetchMangas(provider: MangaPlugin): Promise { - - //fetch from pocket.shonenmagazine.com const mangas1 = await CoreView.FetchMangasMultiPageCSS.call(this, provider, ['/series'], 'div.series-items ul.daily-series > li.daily-series-item > a'); let mangas2 = []; try { - //fetch from shonenmagazine.com - this.URI.href = 'https://shonenmagazine.com'; + // TODO: Do not change a read-only property, this may cause problems for concurrent operations! + this.URI.hostname = 'shonenmagazine.com'; mangas2 = await CoreView.FetchMangasMultiPageCSS.call(this, provider, ['/series/smaga', '/series/bmaga', '/series/others'], 'article.serial-series-contents ul.serial-series-list > li.serial-series-item > a'); - } catch (error) { + } catch { // Do not supress generic errors // } - this.URI.href = 'https://pocket.shonenmagazine.com'; + // TODO: Do not change a read-only property, this may cause problems for concurrent operations! + this.URI.hostname = 'pocket.shonenmagazine.com'; return [...mangas1, ...mangas2]; } } \ No newline at end of file diff --git a/web/src/engine/websites/SundayWebry.ts b/web/src/engine/websites/SundayWebry.ts index a62a735dd8..b6acf53411 100644 --- a/web/src/engine/websites/SundayWebry.ts +++ b/web/src/engine/websites/SundayWebry.ts @@ -75,7 +75,7 @@ export default class extends DecoratableMangaScraper { return { chapters: chapters.reverse(), nextUrl: data.nextUrl }; - } catch (error) { + } catch { // TODO: Do not return incomplete list for generic errors return { chapters: [], nextUrl: '' }; } } diff --git a/web/src/engine/websites/decorators/MangaReaderCMS.ts b/web/src/engine/websites/decorators/MangaReaderCMS.ts index 67ecf9418e..f7cf5e346f 100644 --- a/web/src/engine/websites/decorators/MangaReaderCMS.ts +++ b/web/src/engine/websites/decorators/MangaReaderCMS.ts @@ -93,7 +93,7 @@ export function ChapterPageExtractor(this: MangaScraper, image: HTMLImageElement try { const src = image.dataset['src'].split('://').pop(); return decodeURIComponent(window.atob(src || undefined)); - } catch (error) { + } catch { // TODO: Do not return url for generic errors const src = (image.dataset['src'] || image.src).trim(); return new URL(src, this.URI).href; }