diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5790367..d24c14b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,10 +9,12 @@ jobs: timeout-minutes: 60 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: - node-version: 20 + node-version: 24 - run: yarn install --frozen-lockfile + - run: yarn tsc + - run: yarn format-check - run: yarn playwright install --with-deps - run: yarn playwright test diff --git a/.gitignore b/.gitignore index 0d6c6e2..7f16beb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.log* node_modules/ +/src/manifest.json /tmp/ /dist/ diff --git a/jsconfig.json b/jsconfig.json deleted file mode 100644 index 7cb5f45..0000000 --- a/jsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "compilerOptions": { - "target": "esnext", - "module": "esnext", - "strict": true, - "checkJs": true - }, - "typeAcquisition": { - "include": ["chrome"] - }, - "include": ["src/**/*.js", "tests/**/*.js"], - "exclude": ["node_modules"] -} diff --git a/mise.toml b/mise.toml new file mode 100644 index 0000000..6ea5a7e --- /dev/null +++ b/mise.toml @@ -0,0 +1,2 @@ +[tools] +node = "24" diff --git a/package.json b/package.json index 2c02678..f78e3d2 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,10 @@ "format-check": "prettier --check ." }, "devDependencies": { - "@playwright/test": "^1.45.1", - "@types/chrome": "^0.0.268", - "@types/node": "^20.14.9", - "prettier": "^3.3.2", - "typescript": "^5.5.3" + "@playwright/test": "^1.56.0", + "@types/chrome": "^0.1.24", + "@types/node": "^24.7.2", + "prettier": "^3.6.2", + "typescript": "^5.9.3" } } diff --git a/src/background.js b/src/background.js index 5d407bc..b9415c8 100644 --- a/src/background.js +++ b/src/background.js @@ -13,7 +13,7 @@ chrome.runtime.onInstalled.addListener(() => { const cache = new Map(); -chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { +chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => { sendResponse(cache.get(message)); }); diff --git a/src/content.js b/src/content.js index 0ad805b..7e68349 100644 --- a/src/content.js +++ b/src/content.js @@ -60,10 +60,13 @@ function getComputedBackgroundImages(element, pseudo) { style.getPropertyValue('background-image'), style.getPropertyValue('content'), ]; + /** @type {Set} */ const images = new Set(); for (const value of values) { for (const [, url] of value.matchAll(/url\("(.+?)"\)/g)) { - images.add(url.replaceAll('\\"', '"')); + if (url) { + images.add(url.replaceAll('\\"', '"')); + } } } return images; @@ -85,15 +88,22 @@ if (chrome.runtime) { let x = 0; let y = 0; - document.addEventListener('contextmenu', (event) => { - if (event.target instanceof Element) { - root = event.target.ownerDocument; - } - x = event.clientX; - y = event.clientY; - }); + window.addEventListener( + 'contextmenu', + (event) => { + if (event.shiftKey) { + event.stopPropagation(); + } + if (event.target instanceof Element) { + root = event.target.ownerDocument; + } + x = event.clientX; + y = event.clientY; + }, + { capture: true }, + ); - chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + chrome.runtime.onMessage.addListener((_message, _sender, sendResponse) => { sendResponse([...getBackgroundImages(root, x, y)]); }); } diff --git a/src/manifest.chrome.json b/src/manifest.chrome.json index feb631d..b26f9f8 100644 --- a/src/manifest.chrome.json +++ b/src/manifest.chrome.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "__MSG_extName__", - "version": "3.0.4", + "version": "3.1.0", "description": "__MSG_extDescription__", "default_locale": "en", "permissions": ["contextMenus"], diff --git a/src/manifest.firefox.json b/src/manifest.firefox.json index d0e92a2..f12aab7 100644 --- a/src/manifest.firefox.json +++ b/src/manifest.firefox.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "__MSG_extName__", - "version": "3.0.4", + "version": "3.1.0", "description": "__MSG_extDescription__", "default_locale": "en", "permissions": ["contextMenus"], diff --git a/src/manifest.json b/src/manifest.json deleted file mode 100644 index feb631d..0000000 --- a/src/manifest.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "manifest_version": 3, - "name": "__MSG_extName__", - "version": "3.0.4", - "description": "__MSG_extDescription__", - "default_locale": "en", - "permissions": ["contextMenus"], - "background": { - "service_worker": "background.js" - }, - "content_scripts": [ - { - "matches": ["http://*/*", "https://*/*", "file:///*/*"], - "js": ["content.js"], - "run_at": "document_start", - "all_frames": true - } - ], - "icons": { - "16": "icons/icon16.png", - "32": "icons/icon32.png", - "48": "icons/icon48.png", - "128": "icons/icon128.png" - }, - "incognito": "split" -} diff --git a/src/viewer/error.js b/src/viewer/error.js index f0f9266..dea8474 100644 --- a/src/viewer/error.js +++ b/src/viewer/error.js @@ -8,7 +8,7 @@ export function showError(error) { $('error').classList.remove('hidden'); $('nav').classList.add('hidden'); - if (navigator.userAgent.includes('Gecko')) { + if (navigator.userAgent.includes('Firefox')) { $('error-firefox').classList.remove('hidden'); } } diff --git a/tests/getBackgroundImages.spec.ts b/tests/getBackgroundImages.spec.ts index 4ae80e8..b57aeb8 100644 --- a/tests/getBackgroundImages.spec.ts +++ b/tests/getBackgroundImages.spec.ts @@ -1,6 +1,12 @@ import { test, expect } from '@playwright/test'; import { basename } from 'node:path'; +declare function getBackgroundImages( + root: Document | ShadowRoot, + x: number, + y: number, +): Set; + interface TestCase { title: string; point: [x: number, y: number]; @@ -62,7 +68,6 @@ for (const { title, point, expected, skip } of testCases) { }); const actual = await page.evaluate( - // @ts-ignore ([x, y]) => [...getBackgroundImages(document, x, y)], point, ); diff --git a/tests/locales.test.js b/tests/locales.test.ts similarity index 95% rename from tests/locales.test.js rename to tests/locales.test.ts index 3fceb4d..02f7810 100644 --- a/tests/locales.test.js +++ b/tests/locales.test.ts @@ -20,7 +20,7 @@ test('locales', async (t) => { await t.test(locale, async (t) => { const { default: messages } = await import( import.meta.resolve(`../src/_locales/${locale}/messages.json`), - { assert: { type: 'json' } } + { with: { type: 'json' } } ); for (const key of keys) { diff --git a/tests/tsconfig.json b/tests/tsconfig.json new file mode 100644 index 0000000..c00178d --- /dev/null +++ b/tests/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "module": "nodenext", + "types": ["node"] + }, + "include": ["**/*.ts"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..e7114c4 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + // Environment Settings + "module": "esnext", + "target": "esnext", + "types": ["chrome"], + + // Stricter Typechecking Options + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + + // Style Options + "noImplicitReturns": true, + "noImplicitOverride": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noPropertyAccessFromIndexSignature": true, + + // Recommended Options + "strict": true, + "noUncheckedSideEffectImports": true, + "moduleDetection": "force", + "skipLibCheck": true, + + // Other Options + "allowJs": true, + "checkJs": true, + "noEmit": true + }, + "include": ["src/**/*.js"] +} diff --git a/yarn.lock b/yarn.lock index fcefb93..cc359b3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,17 +2,17 @@ # yarn lockfile v1 -"@playwright/test@^1.45.1": - version "1.45.1" - resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.45.1.tgz#819b90fa43d17000fce5ebd127043fd661938b7a" - integrity sha512-Wo1bWTzQvGA7LyKGIZc8nFSTFf2TkthGIFBR+QVNilvwouGzFd4PYukZe3rvf5PSqjHi1+1NyKSDZKcQWETzaA== +"@playwright/test@^1.56.0": + version "1.56.0" + resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.56.0.tgz#891fe101bddf3eee3dd609e7a145f705dc0f3054" + integrity sha512-Tzh95Twig7hUwwNe381/K3PggZBZblKUe2wv25oIpzWLr6Z0m4KgV1ZVIjnR6GM9ANEqjZD7XsZEa6JL/7YEgg== dependencies: - playwright "1.45.1" + playwright "1.56.0" -"@types/chrome@^0.0.268": - version "0.0.268" - resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.268.tgz#d5855546f30c83e181cadd77127a162c25b480d2" - integrity sha512-7N1QH9buudSJ7sI8Pe4mBHJr5oZ48s0hcanI9w3wgijAlv1OZNUZve9JR4x42dn5lJ5Sm87V1JNfnoh10EnQlA== +"@types/chrome@^0.1.24": + version "0.1.24" + resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.1.24.tgz#71c93111c40a057a8a45c4ab74eb7fe0aaf0048a" + integrity sha512-9iO9HL2bMeGS4C8m6gNFWUyuPE5HEUFk+rGh+7oriUjg+ata4Fc9PoVlu8xvGm7yoo3AmS3J6fAjoFj61NL2rw== dependencies: "@types/filesystem" "*" "@types/har-format" "*" @@ -30,47 +30,47 @@ integrity sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g== "@types/har-format@*": - version "1.2.15" - resolved "https://registry.yarnpkg.com/@types/har-format/-/har-format-1.2.15.tgz#f352493638c2f89d706438a19a9eb300b493b506" - integrity sha512-RpQH4rXLuvTXKR0zqHq3go0RVXYv/YVqv4TnPH95VbwUxZdQlK1EtcMvQvMpDngHbt13Csh9Z4qT9AbkiQH5BA== + version "1.2.16" + resolved "https://registry.yarnpkg.com/@types/har-format/-/har-format-1.2.16.tgz#b71ede8681400cc08b3685f061c31e416cf94944" + integrity sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A== -"@types/node@^20.14.9": - version "20.14.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.9.tgz#12e8e765ab27f8c421a1820c99f5f313a933b420" - integrity sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg== +"@types/node@^24.7.2": + version "24.7.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-24.7.2.tgz#5adf66b6e2ac5cab1d10a2ad3682e359cb652f4a" + integrity sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA== dependencies: - undici-types "~5.26.4" + undici-types "~7.14.0" fsevents@2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== -playwright-core@1.45.1: - version "1.45.1" - resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.45.1.tgz#549a2701556b58245cc75263f9fc2795c1158dc1" - integrity sha512-LF4CUUtrUu2TCpDw4mcrAIuYrEjVDfT1cHbJMfwnE2+1b8PZcFzPNgvZCvq2JfQ4aTjRCCHw5EJ2tmr2NSzdPg== +playwright-core@1.56.0: + version "1.56.0" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.56.0.tgz#14b40ea436551b0bcefe19c5bfb8d1804c83739c" + integrity sha512-1SXl7pMfemAMSDn5rkPeZljxOCYAmQnYLBTExuh6E8USHXGSX3dx6lYZN/xPpTz1vimXmPA9CDnILvmJaB8aSQ== -playwright@1.45.1: - version "1.45.1" - resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.45.1.tgz#aaa6b0d6db14796b599d80c6679e63444e942534" - integrity sha512-Hjrgae4kpSQBr98nhCj3IScxVeVUixqj+5oyif8TdIn2opTCPEzqAqNMeK42i3cWDCVu9MI+ZsGWw+gVR4ISBg== +playwright@1.56.0: + version "1.56.0" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.56.0.tgz#71c533c61da33e95812f8c6fa53960e073548d9a" + integrity sha512-X5Q1b8lOdWIE4KAoHpW3SE8HvUB+ZZsUoN64ZhjnN8dOb1UpujxBtENGiZFE+9F/yhzJwYa+ca3u43FeLbboHA== dependencies: - playwright-core "1.45.1" + playwright-core "1.56.0" optionalDependencies: fsevents "2.3.2" -prettier@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.2.tgz#03ff86dc7c835f2d2559ee76876a3914cec4a90a" - integrity sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA== +prettier@^3.6.2: + version "3.6.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.6.2.tgz#ccda02a1003ebbb2bfda6f83a074978f608b9393" + integrity sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ== -typescript@^5.5.3: - version "5.5.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.3.tgz#e1b0a3c394190838a0b168e771b0ad56a0af0faa" - integrity sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ== +typescript@^5.9.3: + version "5.9.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" + integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== -undici-types@~5.26.4: - version "5.26.5" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" - integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== +undici-types@~7.14.0: + version "7.14.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.14.0.tgz#4c037b32ca4d7d62fae042174604341588bc0840" + integrity sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==