From fb08cb30c9fae1476d26687809c38dd6ab0e2293 Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 26 Feb 2025 21:12:25 -0600 Subject: [PATCH 1/4] fix: Refactor CWS crawler to more safely extract extension details --- src/crawlers/__tests__/chrome-crawler.test.ts | 63 ++-- src/crawlers/chrome-crawler.ts | 312 +++++++++++------- 2 files changed, 212 insertions(+), 163 deletions(-) diff --git a/src/crawlers/__tests__/chrome-crawler.test.ts b/src/crawlers/__tests__/chrome-crawler.test.ts index 0a7889a..d449982 100644 --- a/src/crawlers/__tests__/chrome-crawler.test.ts +++ b/src/crawlers/__tests__/chrome-crawler.test.ts @@ -1,41 +1,34 @@ -import { describe, expect, it } from "bun:test"; +import { beforeEach, describe, expect, it, mock } from "bun:test"; import { crawlExtension } from "../chrome-crawler"; +import { readdir } from "node:fs/promises"; +import { join } from "node:path"; -const githubBetterLineCountsId = "ocfdgncpifmegplaglcnglhioflaimkd"; +const fetchMock = mock(() => { + throw Error("Not mocked"); +}); +globalThis.fetch = fetchMock; -describe("Chrome Web Store Crawler", () => { - it("should load and crawl an extension ID correctly", async () => { - const res = await crawlExtension(githubBetterLineCountsId, "en"); +describe("Chrome Web Store Crawler", async () => { + const fixturesDir = join(import.meta.dir, "fixtures/chrome-web-store"); + const testFiles = (await readdir(fixturesDir)) + .filter((file) => !file.startsWith(".")) + .toSorted(); + const getExtensionIdFromFile = (file: string) => + file.match(/.*-([a-z]+)\.html/)![1]; - expect(res).toEqual({ - iconUrl: - "https://lh3.googleusercontent.com/GcffNyCJaxT2G9dsQCJHhUEMlu_E0vEzph5cLPrQj7UHKat7QyCzGu69Dmp_DDUL8rY-bPMFJceQarS1wcqdwTalTg=s256", - id: githubBetterLineCountsId, - lastUpdated: expect.any(String), - longDescription: expect.stringContaining("Isn't it annoying when you"), - name: "GitHub: Better Line Counts", - rating: expect.any(Number), - reviewCount: expect.any(Number), - shortDescription: "Remove generated files from GitHub line counts", - storeUrl: expect.stringContaining( - "https://chromewebstore.google.com/detail/github-better-line-counts/ocfdgncpifmegplaglcnglhioflaimkd", - ), - version: expect.any(String), - weeklyActiveUsers: expect.any(Number), - screenshots: [ - { - index: 0, - indexUrl: - "http://localhost:3000/api/rest/chrome-extensions/ocfdgncpifmegplaglcnglhioflaimkd/screenshots/0", - rawUrl: expect.any(String), - }, - { - index: 1, - indexUrl: - "http://localhost:3000/api/rest/chrome-extensions/ocfdgncpifmegplaglcnglhioflaimkd/screenshots/1", - rawUrl: expect.any(String), - }, - ], - }); + beforeEach(() => { + fetchMock.mockReset(); }); + + it.each(testFiles)( + "should extract extension details from %s", + async (file) => { + const id = getExtensionIdFromFile(file); + globalThis.fetch = mock(() => + Promise.resolve(new Response(Bun.file(join(fixturesDir, file)))), + ); + const res = await crawlExtension(id, "en", true); + expect(res).toMatchSnapshot(); + }, + ); }); diff --git a/src/crawlers/chrome-crawler.ts b/src/crawlers/chrome-crawler.ts index 648d093..edab813 100644 --- a/src/crawlers/chrome-crawler.ts +++ b/src/crawlers/chrome-crawler.ts @@ -5,6 +5,7 @@ import { buildScreenshotUrl } from "../utils/urls"; export async function crawlExtension( id: string, lang: string, + canGenerateTestFixture = false, ): Promise { consola.info("Crawling " + id); const url = `https://chromewebstore.google.com/detail/${id}?hl=${lang}`; @@ -19,128 +20,129 @@ export async function crawlExtension( if (res.status !== 200) return; const html = await res.text(); - const { document } = parseHTML(html); - - // Uncomment to debug HTML - // Bun.write("chrome.html", document.documentElement.outerHTML); - - // Basic metadata - const name = metaContent(document, "property=og:title")?.replace( - / - Chrome Web Store$/, - "", - ); - const storeUrl = metaContent(document, "property=og:url"); - const iconUrl = metaContent(document, "property=og:image")?.replace( - /=.+?$/, - "=s256", - ); - const shortDescription = metaContent(document, "property=og:description"); - - // Grab the main sections that contain content - const sections = (document as HTMLElement).querySelectorAll( - "main > * > section", - ); - const header: HTMLElement = sections[0].querySelector( - "section > section > div", + if (!canGenerateTestFixture) { + // Uncomment to debug HTML or generate new test fixture + // const date = new Date(); + // const dateString = `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`; + // Bun.write( + // `src/crawlers/__tests__/fixtures/chrome-web-store/.new/${dateString}-${id}.html`, + // html, + // ); + } + + const parsed = parseHTML(html); + const document = parsed.document as HTMLElement; + + if (document.querySelector("section") == null) { + // Extension is unavailable, ID no longer exists + return undefined; + } + + const name = tryExtract("name", validateNonEmptyString, [ + () => + metaContent(document, "property=og:title")?.replace( + / - Chrome Web Store$/, + "", + ), + ]); + const storeUrl = tryExtract("storeUrl", validateNonEmptyString, [ + () => metaContent(document, "property=og:url"), + ]); + const iconUrl = tryExtract("iconUrl", validateNonEmptyString, [ + () => metaContent(document, "property=og:image")?.replace(/=.+?$/, "=s256"), + ]); + const shortDescription = tryExtract( + "shortDescription", + validateNonEmptyString, + [() => metaContent(document, "property=og:description")], ); - const description: HTMLElement = sections[2]; - const details: HTMLElement = sections[3]; - // Header - // userRowCount.outerHTHML: - //
- // ... - // ... - // 73 users - //
- // Remove the anchors and extract "73" from the text content - const userCountRow = header.querySelector(":scope > div:last-child"); - userCountRow - .querySelectorAll("a") - .forEach((anchor: HTMLAnchorElement) => anchor.remove()); - const weeklyActiveUsers = (userCountRow.textContent as string) - // "XYZ+ users" - .replace(" users", "") - .replaceAll(",", "") - .replace("+", "") - .trim(); - - // ratingRow.outerHTML: - // - // - // 5.0 - // - // (

2 ratings

)
- //
- //
- const ratingRow = header.querySelector( - "div:first-child > div:nth-child(2) > span:nth-child(3)", - ); - const rating = - ratingRow != null - ? extractNumber( - ratingRow.querySelector("span:first-child > span:first-child") - .textContent, + const weeklyActiveUsers = tryExtract("weeklyActiveUsers", validateInt, [ + () => { + const userCountRow = document.querySelector( + "main > * > section:first-child > section > div > div:last-child", + ) as HTMLElement | null; + removeAnchorChildren(userCountRow); + return ( + userCountRow?.textContent + // "W,XYZ+ users" + ?.replace(" users", "") + .replaceAll(",", "") + .replace("+", "") + ); + }, + ]); + + const rating = tryExtract("rating", validateFloat, [ + () => + document.querySelector( + "main > * > section:first-child > section > div > div:nth-child(2) > span > span > span", + )?.textContent, + () => + document.querySelector("body").textContent.includes("No ratings") + ? 0 + : undefined, + ]); + + const reviewCount = tryExtract("reviewCount", validateInt, [ + () => + document + .querySelector( + "main > * > section:first-child > section > div > div:nth-child(2) > span > span > span:last-child > a", ) - : 0; - const reviewCount = - ratingRow != null - ? extractNumber(ratingRow.querySelector("p").textContent) - : 0; - - // Details - - const detailItems = details.querySelectorAll("li > div:last-child"); - const version = detailItems[0].textContent.trim(); - const lastUpdated = detailItems[1].textContent.trim(); - - // Description - - const longDescription = description - .querySelector("p:last-child") - .textContent.replaceAll("\n\n", "\n"); - - // const longDescription = document - // .querySelector("div[itemprop=description]") - // ?.nextElementSibling?.textContent?.trim(); - // - // const ratingDiv = document.querySelector(".rsw-stars"); - // const rating = extractNumber(ratingDiv.title); // "Average rating: 4.78 stars" - // const reviewCount = extractNumber(ratingDiv.textContent); // "(1024)" - - //
- const screenshots = [...document.querySelectorAll("div[data-media-url]")] - .filter((div) => div.getAttribute("data-is-video") === "false") - .map((div) => { - const index = Number(div.getAttribute("data-slide-index") || -1); - return { - index, - rawUrl: div.getAttribute("data-media-url") + "=s1280", // "s1280" gets the full resolution - indexUrl: buildScreenshotUrl("chrome-extensions", id, index), - }; - }); - - if (name == null) return; - if (storeUrl == null) return; - if (iconUrl == null) return; - if (weeklyActiveUsers == null) return; - if (lastUpdated == null) return; - if (version == null) return; - if (shortDescription == null) return; - if (longDescription == null) return; - if ( - screenshots.some( - (screenshot) => screenshot.index === -1 || !screenshot.rawUrl, - ) - ) - return; + ?.textContent?.replace(" ratings", ""), + () => + document.querySelector("body").textContent.includes("No ratings") + ? 0 + : undefined, + ]); + + const version = tryExtract("version", validateNonEmptyString, [ + () => { + const listItems = [ + ...document.querySelectorAll("main > * > section:nth-child(5) li"), + ]; + const li = listItems.find((item) => item.textContent.includes("Version")); + return li?.querySelector(":scope > *:last-child")?.textContent; + }, + ]); + + const lastUpdated = tryExtract("lastUpdated", validateNonEmptyString, [ + () => { + const listItems = [ + ...document.querySelectorAll("main > * > section:nth-child(5) li"), + ]; + const li = listItems.find((item) => item.textContent.includes("Updated")); + return li?.querySelector(":scope > *:last-child")?.textContent; + }, + ]); + + const longDescription = tryExtract( + "longDescription", + validateNonEmptyString, + [ + () => + document + .querySelector("main > * > section:nth-child(3) p:last-child") + ?.textContent?.replaceAll("\n\n", "\n"), + ], + ); + + const screenshots = tryExtract("screenshots", validateGqlScreenshots, [ + () => + [...document.querySelectorAll("div[data-media-url]")] + .filter((div) => div.getAttribute("data-is-video") === "false") + .map((div) => { + const index = parseInt(div.getAttribute("data-slide-index")); + return { + index, + rawUrl: div.getAttribute("data-media-url") + "=s1280", // "s1280" gets the full resolution + indexUrl: buildScreenshotUrl("chrome-extensions", id, index), + }; + }), + ]); const result: Gql.ChromeExtension = { id, @@ -160,25 +162,79 @@ export async function crawlExtension( return result; } -function metaContent(document: any, attrSelector: string): string | undefined { +function metaContent( + document: HTMLElement, + attrSelector: string, +): string | undefined { return document .querySelector(`meta[${attrSelector}]`) ?.getAttribute("content") .trim(); } -function nextSpanText(document: any, text: string): string | undefined { - const spans: any[] = Array.from(document.querySelectorAll("span")); - const span = spans.find((span: any) => span.textContent?.startsWith(text)); - return span.nextElementSibling.textContent.trim(); +/** Try each of the different functions, collecting errors, and return the first value that can be parsed correctly. If no options succeed, return an error containing all the errors in it's cause. */ +function tryExtract( + field: string, + validate: (value: any) => T, + extractors: (() => any)[], +): T { + const errors: Error[] = []; + for (const extract of extractors) { + try { + const result = extract(); + return validate(result); + } catch (error) { + errors.push(error as Error); + } + } + errors.forEach((err) => console.error(err)); + throw new Error(`Could not extract "${field}" from HTML`, { cause: errors }); +} + +function validateInt(value: any): number { + const int = parseInt(value); + if (isNaN(int)) throw Error(`"${value}" was not an integer`); + return int; } -function extractNumber(text: string): number | undefined { - const res = /([0-9\.,]+)/.exec(text)?.[1]; - if (res == null) return; +function validateFloat(value: any): number { + const float = parseFloat(value); + if (isNaN(float)) throw Error(`"${value}" was not an float`); + return float; +} + +function validateNonEmptyString(value: any): string { + if (typeof value !== "string" || value.trim().length === 0) { + throw new Error(`"${value}" was not a non-empty string`); + } + return value.trim(); +} - const num = Number(res); - if (isNaN(num)) return; +function validateGqlScreenshots(value: any): Gql.Screenshot[] { + if (!Array.isArray(value)) { + throw new Error(`Screenshots must be an array`); + } + if (value.length === 0) { + throw new Error(`Screenshots array cannot be empty`); + } + return value.map((screenshot: any) => { + if (typeof screenshot !== "object" || screenshot === null) { + throw new Error(`Each screenshot must be an object`); + } + const index = validateInt(screenshot.index); + if (index < 0) throw Error("Screenshot index missing"); + const rawUrl = validateNonEmptyString(screenshot.rawUrl); + const indexUrl = validateNonEmptyString(screenshot.indexUrl); + return { + index, + rawUrl, + indexUrl, + }; + }); +} - return num; +function removeAnchorChildren(element: HTMLElement | null | undefined): void { + element + ?.querySelectorAll("a") + .forEach((anchor: HTMLAnchorElement) => anchor.remove()); } From c31f4440d8a586e5baacce1d702a26294fcc29a5 Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 26 Feb 2025 21:15:11 -0600 Subject: [PATCH 2/4] Add gitattributes --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index afab2ea..2f76d3c 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,7 @@ tsconfig.tsbuildinfo # .env files .env .env.* + +# Other Files +src/crawlers/__tests__/fixtures/chrome-web-store/.new/* +!src/crawlers/__tests__/fixtures/chrome-web-store/.new/.keep From 8180501fa8c525cf244bd57670057b3f24e062a4 Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 26 Feb 2025 21:19:46 -0600 Subject: [PATCH 3/4] cleanup --- .gitattributes | 1 + .prettierignore | 1 + .../__snapshots__/chrome-crawler.test.ts.snap | 321 ++++++++++++++++++ .../__tests__/chrome-crawler.e2e.test.ts | 41 +++ .../fixtures/chrome-web-store/.new/.keep | 0 ...2-26-kofbbilhmnkcmibjbioafflgmpkbnmme.html | 140 ++++++++ ...2-26-oadbjpccljkplmhnjekgjamejnbadlne.html | 123 +++++++ ...2-26-ocfdgncpifmegplaglcnglhioflaimkd.html | 137 ++++++++ ...2-26-odffpjnpocjfcaclnenaaaddghkgijdb.html | 163 +++++++++ src/crawlers/chrome-crawler.ts | 12 +- 10 files changed, 933 insertions(+), 6 deletions(-) create mode 100644 .gitattributes create mode 100644 .prettierignore create mode 100644 src/crawlers/__tests__/__snapshots__/chrome-crawler.test.ts.snap create mode 100644 src/crawlers/__tests__/chrome-crawler.e2e.test.ts create mode 100644 src/crawlers/__tests__/fixtures/chrome-web-store/.new/.keep create mode 100644 src/crawlers/__tests__/fixtures/chrome-web-store/2025-02-26-kofbbilhmnkcmibjbioafflgmpkbnmme.html create mode 100644 src/crawlers/__tests__/fixtures/chrome-web-store/2025-02-26-oadbjpccljkplmhnjekgjamejnbadlne.html create mode 100644 src/crawlers/__tests__/fixtures/chrome-web-store/2025-02-26-ocfdgncpifmegplaglcnglhioflaimkd.html create mode 100644 src/crawlers/__tests__/fixtures/chrome-web-store/2025-02-26-odffpjnpocjfcaclnenaaaddghkgijdb.html diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..0329aa2 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +src/crawlers/__tests__/fixtures/chrome-web-store/*.html linguist-generated=true diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..f47c108 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +**/__tests__/fixtures/** diff --git a/src/crawlers/__tests__/__snapshots__/chrome-crawler.test.ts.snap b/src/crawlers/__tests__/__snapshots__/chrome-crawler.test.ts.snap new file mode 100644 index 0000000..a8212c2 --- /dev/null +++ b/src/crawlers/__tests__/__snapshots__/chrome-crawler.test.ts.snap @@ -0,0 +1,321 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`Chrome Web Store Crawler should extract extension details from 2025-02-26-odffpjnpocjfcaclnenaaaddghkgijdb.html 1`] = ` +{ + "iconUrl": "https://lh3.googleusercontent.com/Tr_1QA0fh5O5L5Bi3Moz6bPKxu1fC7aMrH_dCcKeLiBhjLCGmn46j59unBNxZdQ3fEymS_YXj2CHdsM-FE5vNzSEPg=s256", + "id": "odffpjnpocjfcaclnenaaaddghkgijdb", + "lastUpdated": "January 20, 2025", + "longDescription": +"Preview links in a popup without opening new tabs. Quickly search using text selection, select text using selection handler and search using keyboard shortcuts while staying on the same page. +✨ Preview Links Instantly +Hover over or click on links to preview content in a popup, without navigating away from your current tab. Save time and stay focused. +🔍 Search Smarter +Search the web directly from any page without opening a new tab. Use the selection popup or open a quick search bar using keyboard shortcut to search anything instantly. +🤖 Turn Your AI Into an Assistant +Use your favorite AI tools like ChatGPT or Gemini automated by Blync. Set action items with prompt, on any text selection the item shows in popup and clicking on it opens AI prompt in popup or sidebar. +👉 Is it safe to use Blync? +Absolutely! Blync is 100% secure. +✅ It’s serverless and runs entirely within your browser. +✅ We don’t track, store, or access your data. +✅ Everything stays private and local. +👉 Does Blync Link Preview work on all websites? +✅ Yes! Blync works seamlessly on any website. +👉 Do I need an AI account to use the AI Assistant? +✅ Yes. Blync doesn’t provide AI services. +✅ Blync automates the process, making it faster and easier for you to access AI features. +👉 What AI agents does Blync support? +✅ Currently, Blync supports: ChatGPT, Gemini and Claude +💡 Why Choose Blync? +✅ Stay Focused: No more tab overload or switching between windows. +✅ Boost Productivity: Access essential tools and information faster. +✅ Seamless Integration: Works effortlessly with the tools you already love. +✅ Whether you're working, learning or just exploring the web Blync keeps everything within reach and helps you stay in the flow. +➡️ Install Blync now and make every click count!" +, + "name": "Blync: Preview Links, Selection Search and AI Sidebar & Popup", + "rating": 5, + "reviewCount": 2, + "screenshots": [ + { + "index": 5, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/5", + "rawUrl": "https://lh3.googleusercontent.com/MXf271aHeP9deq2VjO0-_QDU-VSKgu81hcc9iKNMbAg74sI1RnvOb7Esgy95P-NTrQ7vVJtW2fMOPnTYv8pnbnMEng=s1280", + }, + { + "index": 1, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/1", + "rawUrl": "https://lh3.googleusercontent.com/WVvtpr2Wzfr4zT34Z_Gy39ZL-I8UOZ5uReOAz-vmtC8lT4hebXlnur7y0bAWzXd6xihMbTP_PfZgQN_C4MfhixI2=s1280", + }, + { + "index": 2, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/2", + "rawUrl": "https://lh3.googleusercontent.com/e3JEIuiGu_dI70zGBPBXzL9YNZuHy8nItCoEKJSql56CcUVyIQC9Lor8HkeScWE2-qAFw5DLZQijXEfNxlInSoLl0DE=s1280", + }, + { + "index": 3, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/3", + "rawUrl": "https://lh3.googleusercontent.com/O9OuxL28A3GOp5-R8u2kyV9wkOchMTx0ZNWraAz93gqThZRwVpCbaEW0VL1bZ0dKaKr0ReN0qJYJfSFAPOkCuOGA=s1280", + }, + { + "index": 4, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/4", + "rawUrl": "https://lh3.googleusercontent.com/vfXSijB8FQ4hkARrKdJJfXe3b_ahd8yXsSuAbbKNwU9yondg0SIj1fenPJRosimBMo8bi1hj99lypD1CS1y1KhJE=s1280", + }, + { + "index": 5, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/5", + "rawUrl": "https://lh3.googleusercontent.com/MXf271aHeP9deq2VjO0-_QDU-VSKgu81hcc9iKNMbAg74sI1RnvOb7Esgy95P-NTrQ7vVJtW2fMOPnTYv8pnbnMEng=s1280", + }, + { + "index": 1, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/1", + "rawUrl": "https://lh3.googleusercontent.com/WVvtpr2Wzfr4zT34Z_Gy39ZL-I8UOZ5uReOAz-vmtC8lT4hebXlnur7y0bAWzXd6xihMbTP_PfZgQN_C4MfhixI2=s1280", + }, + { + "index": 2, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/2", + "rawUrl": "https://lh3.googleusercontent.com/e3JEIuiGu_dI70zGBPBXzL9YNZuHy8nItCoEKJSql56CcUVyIQC9Lor8HkeScWE2-qAFw5DLZQijXEfNxlInSoLl0DE=s1280", + }, + { + "index": 3, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/3", + "rawUrl": "https://lh3.googleusercontent.com/O9OuxL28A3GOp5-R8u2kyV9wkOchMTx0ZNWraAz93gqThZRwVpCbaEW0VL1bZ0dKaKr0ReN0qJYJfSFAPOkCuOGA=s1280", + }, + { + "index": 4, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/4", + "rawUrl": "https://lh3.googleusercontent.com/vfXSijB8FQ4hkARrKdJJfXe3b_ahd8yXsSuAbbKNwU9yondg0SIj1fenPJRosimBMo8bi1hj99lypD1CS1y1KhJE=s1280", + }, + { + "index": 5, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/5", + "rawUrl": "https://lh3.googleusercontent.com/MXf271aHeP9deq2VjO0-_QDU-VSKgu81hcc9iKNMbAg74sI1RnvOb7Esgy95P-NTrQ7vVJtW2fMOPnTYv8pnbnMEng=s1280", + }, + ], + "shortDescription": "Preview links, search the web without leaving your current tab and use AI (ChatGPT, Gemini) in popup and sidebar", + "storeUrl": "https://chromewebstore.google.com/detail/blync-preview-links-selec/odffpjnpocjfcaclnenaaaddghkgijdb?hl=en", + "version": "0.3.3", + "weeklyActiveUsers": 40, +} +`; + +exports[`Chrome Web Store Crawler should extract extension details from 2025-02-26-fonflmjnjbkigocpoommgmhljdpljain.html 1`] = ` +{ + "iconUrl": "https://lh3.googleusercontent.com/Tr_1QA0fh5O5L5Bi3Moz6bPKxu1fC7aMrH_dCcKeLiBhjLCGmn46j59unBNxZdQ3fEymS_YXj2CHdsM-FE5vNzSEPg=s256", + "id": "fonflmjnjbkigocpoommgmhljdpljain", + "lastUpdated": "January 20, 2025", + "longDescription": +"Preview links in a popup without opening new tabs. Quickly search using text selection, select text using selection handler and search using keyboard shortcuts while staying on the same page. +✨ Preview Links Instantly +Hover over or click on links to preview content in a popup, without navigating away from your current tab. Save time and stay focused. +🔍 Search Smarter +Search the web directly from any page without opening a new tab. Use the selection popup or open a quick search bar using keyboard shortcut to search anything instantly. +🤖 Turn Your AI Into an Assistant +Use your favorite AI tools like ChatGPT or Gemini automated by Blync. Set action items with prompt, on any text selection the item shows in popup and clicking on it opens AI prompt in popup or sidebar. +👉 Is it safe to use Blync? +Absolutely! Blync is 100% secure. +✅ It’s serverless and runs entirely within your browser. +✅ We don’t track, store, or access your data. +✅ Everything stays private and local. +👉 Does Blync Link Preview work on all websites? +✅ Yes! Blync works seamlessly on any website. +👉 Do I need an AI account to use the AI Assistant? +✅ Yes. Blync doesn’t provide AI services. +✅ Blync automates the process, making it faster and easier for you to access AI features. +👉 What AI agents does Blync support? +✅ Currently, Blync supports: ChatGPT, Gemini and Claude +💡 Why Choose Blync? +✅ Stay Focused: No more tab overload or switching between windows. +✅ Boost Productivity: Access essential tools and information faster. +✅ Seamless Integration: Works effortlessly with the tools you already love. +✅ Whether you're working, learning or just exploring the web Blync keeps everything within reach and helps you stay in the flow. +➡️ Install Blync now and make every click count!" +, + "name": "Blync: Preview Links, Selection Search and AI Sidebar & Popup", + "rating": 5, + "reviewCount": 2, + "screenshots": [ + { + "index": 5, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/fonflmjnjbkigocpoommgmhljdpljain/screenshots/5", + "rawUrl": "https://lh3.googleusercontent.com/MXf271aHeP9deq2VjO0-_QDU-VSKgu81hcc9iKNMbAg74sI1RnvOb7Esgy95P-NTrQ7vVJtW2fMOPnTYv8pnbnMEng=s1280", + }, + { + "index": 1, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/fonflmjnjbkigocpoommgmhljdpljain/screenshots/1", + "rawUrl": "https://lh3.googleusercontent.com/WVvtpr2Wzfr4zT34Z_Gy39ZL-I8UOZ5uReOAz-vmtC8lT4hebXlnur7y0bAWzXd6xihMbTP_PfZgQN_C4MfhixI2=s1280", + }, + { + "index": 2, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/fonflmjnjbkigocpoommgmhljdpljain/screenshots/2", + "rawUrl": "https://lh3.googleusercontent.com/e3JEIuiGu_dI70zGBPBXzL9YNZuHy8nItCoEKJSql56CcUVyIQC9Lor8HkeScWE2-qAFw5DLZQijXEfNxlInSoLl0DE=s1280", + }, + { + "index": 3, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/fonflmjnjbkigocpoommgmhljdpljain/screenshots/3", + "rawUrl": "https://lh3.googleusercontent.com/O9OuxL28A3GOp5-R8u2kyV9wkOchMTx0ZNWraAz93gqThZRwVpCbaEW0VL1bZ0dKaKr0ReN0qJYJfSFAPOkCuOGA=s1280", + }, + { + "index": 4, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/fonflmjnjbkigocpoommgmhljdpljain/screenshots/4", + "rawUrl": "https://lh3.googleusercontent.com/vfXSijB8FQ4hkARrKdJJfXe3b_ahd8yXsSuAbbKNwU9yondg0SIj1fenPJRosimBMo8bi1hj99lypD1CS1y1KhJE=s1280", + }, + { + "index": 5, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/fonflmjnjbkigocpoommgmhljdpljain/screenshots/5", + "rawUrl": "https://lh3.googleusercontent.com/MXf271aHeP9deq2VjO0-_QDU-VSKgu81hcc9iKNMbAg74sI1RnvOb7Esgy95P-NTrQ7vVJtW2fMOPnTYv8pnbnMEng=s1280", + }, + { + "index": 1, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/fonflmjnjbkigocpoommgmhljdpljain/screenshots/1", + "rawUrl": "https://lh3.googleusercontent.com/WVvtpr2Wzfr4zT34Z_Gy39ZL-I8UOZ5uReOAz-vmtC8lT4hebXlnur7y0bAWzXd6xihMbTP_PfZgQN_C4MfhixI2=s1280", + }, + { + "index": 2, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/fonflmjnjbkigocpoommgmhljdpljain/screenshots/2", + "rawUrl": "https://lh3.googleusercontent.com/e3JEIuiGu_dI70zGBPBXzL9YNZuHy8nItCoEKJSql56CcUVyIQC9Lor8HkeScWE2-qAFw5DLZQijXEfNxlInSoLl0DE=s1280", + }, + { + "index": 3, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/fonflmjnjbkigocpoommgmhljdpljain/screenshots/3", + "rawUrl": "https://lh3.googleusercontent.com/O9OuxL28A3GOp5-R8u2kyV9wkOchMTx0ZNWraAz93gqThZRwVpCbaEW0VL1bZ0dKaKr0ReN0qJYJfSFAPOkCuOGA=s1280", + }, + { + "index": 4, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/fonflmjnjbkigocpoommgmhljdpljain/screenshots/4", + "rawUrl": "https://lh3.googleusercontent.com/vfXSijB8FQ4hkARrKdJJfXe3b_ahd8yXsSuAbbKNwU9yondg0SIj1fenPJRosimBMo8bi1hj99lypD1CS1y1KhJE=s1280", + }, + { + "index": 5, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/fonflmjnjbkigocpoommgmhljdpljain/screenshots/5", + "rawUrl": "https://lh3.googleusercontent.com/MXf271aHeP9deq2VjO0-_QDU-VSKgu81hcc9iKNMbAg74sI1RnvOb7Esgy95P-NTrQ7vVJtW2fMOPnTYv8pnbnMEng=s1280", + }, + ], + "shortDescription": "Preview links, search the web without leaving your current tab and use AI (ChatGPT, Gemini) in popup and sidebar", + "storeUrl": "https://chromewebstore.google.com/detail/blync-preview-links-selec/odffpjnpocjfcaclnenaaaddghkgijdb?hl=en", + "version": "0.3.3", + "weeklyActiveUsers": 40, +} +`; + +exports[`Chrome Web Store Crawler should extract extension details from 2025-02-26-ocfdgncpifmegplaglcnglhioflaimkd.html 1`] = ` +{ + "iconUrl": "https://lh3.googleusercontent.com/GcffNyCJaxT2G9dsQCJHhUEMlu_E0vEzph5cLPrQj7UHKat7QyCzGu69Dmp_DDUL8rY-bPMFJceQarS1wcqdwTalTg=s256", + "id": "ocfdgncpifmegplaglcnglhioflaimkd", + "lastUpdated": "October 20, 2024", + "longDescription": +"Isn't it annoying when you open a small PR, but when you look at the diff, it's +2000 -16 because you installed a new library? Or what if you had to review that PR, don't line counts like that dissuade you from starting the review? +In reality, lots of code is generated nowadays and GitHub's line counts are not representative of a PR's true size. +This extension subtracts generated files from the total line counts, giving you a better idea of how big a PR really is. That's it. That's all it does. +Generated files are detected from the branch's root .gitattributes file. See GitHub's docs to learn how to mark a file as generated: https://docs.github.com/en/repositories/working-with-files/managing-files/customizing-how-changed-files-appear-on-github +For a simple example, checkout this extension's .gitattributes file! https://github.com/aklinker1/github-better-line-counts/blob/main/.gitattributes +--- +The extension is open source. Feel free to contribute if you have any ideas or just star it 😀 +https://github.com/aklinker1/github-better-line-counts" +, + "name": "GitHub: Better Line Counts", + "rating": 5, + "reviewCount": 4, + "screenshots": [ + { + "index": 0, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/ocfdgncpifmegplaglcnglhioflaimkd/screenshots/0", + "rawUrl": "https://lh3.googleusercontent.com/GUgh0ThX2FDPNvbaumYl4DqsUhsbYiCe-Hut9FoVEnkmTrXyA-sHbMk5jmZTj_t-dDP8rAmy6X6a6GNTCn9F8zo4VYU=s1280", + }, + { + "index": 1, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/ocfdgncpifmegplaglcnglhioflaimkd/screenshots/1", + "rawUrl": "https://lh3.googleusercontent.com/qRi-kO0il8W6CnWa_-7oFzCwWKwr73w607I-rpYF9MM27omsuoN0k4dkgBbBECD3vZszdTSkQnoW9sywsfvAQ_7M9Q=s1280", + }, + ], + "shortDescription": "Remove generated files from GitHub line counts", + "storeUrl": "https://chromewebstore.google.com/detail/github-better-line-counts/ocfdgncpifmegplaglcnglhioflaimkd?hl=en", + "version": "1.7.1", + "weeklyActiveUsers": 251, +} +`; + +exports[`Chrome Web Store Crawler should extract extension details from 2025-02-26-kofbbilhmnkcmibjbioafflgmpkbnmme.html 1`] = ` +{ + "iconUrl": "https://lh3.googleusercontent.com/5JM36LnZNUMFJCJlc7Qqk8QWFYIwfqRYmAdsUhG8inAnw5LNub7ukwq37-Tw3jiDIyBbz-CAKg_ZXgERZ_gJu-0GDg=s256", + "id": "kofbbilhmnkcmibjbioafflgmpkbnmme", + "lastUpdated": "January 10, 2025", + "longDescription": +"HTML to Markdown – a straightforward Chrome extension designed to convert most web pages into markdown format effortlessly. +As a developer, I often found myself needing to pull high-quality documentation from websites and reformat it for use with AI chat tools. That's why I created the "HTML to Markdown" Chrome extension. This free tool quickly converts web page content into clean Markdown, making it perfect for feeding into large language models or any other AI powered chats. +Use Cases: +Bloggers & Content Creators: Easily grab content from articles or research pages for your own posts. +Developers: Copy code snippets and documentation into README files, project notes, or for use with large language models. +Students & Researchers: Quickly extract information from online resources for papers and reports. +Technical Writers: Convert website content to create guides and documentation. +Note Taking: Convert articles or documentation into markdown notes. +AI Integration: Extract and convert relevant website information into markdown for input into AI powered chat tools." +, + "name": "HTML to Markdown", + "rating": 0, + "reviewCount": 0, + "screenshots": [ + { + "index": 5, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/5", + "rawUrl": "https://lh3.googleusercontent.com/SwOe0XmN3ZkCkJQ4IF1a1i2qqCsw0b7I-Ez6VSQflAvafwiJhGbdRW6YAKNX8fAwdv159XogYSFc5Uykgt1fsYSv=s1280", + }, + { + "index": 1, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/1", + "rawUrl": "https://lh3.googleusercontent.com/vDnkawI_rgAd5ou0gasjR5CQbm-HmbTj9SBTdlpxU_-ri1jeE9dz0qNnSOijaboiUd4tuwQBGg-ujIO-znDN4-InVZA=s1280", + }, + { + "index": 2, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/2", + "rawUrl": "https://lh3.googleusercontent.com/F9H2EBxfqxrriMb0zdjoFIiCufav1oV8QI-enLqT9AEuL0nPFJLaj0286R2UH_ekxLoVa-yO76f5sSDOAjEuORrRLg=s1280", + }, + { + "index": 3, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/3", + "rawUrl": "https://lh3.googleusercontent.com/kkK_WlsYeZNGK-sU-4nvH-sEXtx7xUMuqpsK-IHq29IUkmyC3C40n1DDsDdt0f6-LgbKGL_obH_Cse-zJWJPHk-OLQ=s1280", + }, + { + "index": 4, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/4", + "rawUrl": "https://lh3.googleusercontent.com/VJy9hYOMvlmwp2FhtrpIm8hNwwJVZWl-V4dZVl2cCl4aD8xNyuHaEgdm5bfjMBiZJXI0ysxul8P1GAyFaJrRvjDSuMY=s1280", + }, + { + "index": 5, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/5", + "rawUrl": "https://lh3.googleusercontent.com/SwOe0XmN3ZkCkJQ4IF1a1i2qqCsw0b7I-Ez6VSQflAvafwiJhGbdRW6YAKNX8fAwdv159XogYSFc5Uykgt1fsYSv=s1280", + }, + { + "index": 1, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/1", + "rawUrl": "https://lh3.googleusercontent.com/vDnkawI_rgAd5ou0gasjR5CQbm-HmbTj9SBTdlpxU_-ri1jeE9dz0qNnSOijaboiUd4tuwQBGg-ujIO-znDN4-InVZA=s1280", + }, + { + "index": 2, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/2", + "rawUrl": "https://lh3.googleusercontent.com/F9H2EBxfqxrriMb0zdjoFIiCufav1oV8QI-enLqT9AEuL0nPFJLaj0286R2UH_ekxLoVa-yO76f5sSDOAjEuORrRLg=s1280", + }, + { + "index": 3, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/3", + "rawUrl": "https://lh3.googleusercontent.com/kkK_WlsYeZNGK-sU-4nvH-sEXtx7xUMuqpsK-IHq29IUkmyC3C40n1DDsDdt0f6-LgbKGL_obH_Cse-zJWJPHk-OLQ=s1280", + }, + { + "index": 4, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/4", + "rawUrl": "https://lh3.googleusercontent.com/VJy9hYOMvlmwp2FhtrpIm8hNwwJVZWl-V4dZVl2cCl4aD8xNyuHaEgdm5bfjMBiZJXI0ysxul8P1GAyFaJrRvjDSuMY=s1280", + }, + { + "index": 5, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/5", + "rawUrl": "https://lh3.googleusercontent.com/SwOe0XmN3ZkCkJQ4IF1a1i2qqCsw0b7I-Ez6VSQflAvafwiJhGbdRW6YAKNX8fAwdv159XogYSFc5Uykgt1fsYSv=s1280", + }, + ], + "shortDescription": "Convert webpages to Markdown. Easily convert headers, text, tables or code snippets to Markdown.", + "storeUrl": "https://chromewebstore.google.com/detail/html-to-markdown/kofbbilhmnkcmibjbioafflgmpkbnmme?hl=en", + "version": "0.0.13", + "weeklyActiveUsers": 77, +} +`; + +exports[`Chrome Web Store Crawler should extract extension details from 2025-02-26-oadbjpccljkplmhnjekgjamejnbadlne.html 1`] = `undefined`; diff --git a/src/crawlers/__tests__/chrome-crawler.e2e.test.ts b/src/crawlers/__tests__/chrome-crawler.e2e.test.ts new file mode 100644 index 0000000..4f4ae0d --- /dev/null +++ b/src/crawlers/__tests__/chrome-crawler.e2e.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, it } from "bun:test"; +import { crawlExtension } from "../chrome-crawler"; + +const githubBetterLineCountsId = "ocfdgncpifmegplaglcnglhioflaimkd"; + +describe("Chrome Web Store Crawler E2E", () => { + it("should load and crawl an extension ID correctly", async () => { + const res = await crawlExtension(githubBetterLineCountsId, "en", true); + + expect(res).toEqual({ + iconUrl: + "https://lh3.googleusercontent.com/GcffNyCJaxT2G9dsQCJHhUEMlu_E0vEzph5cLPrQj7UHKat7QyCzGu69Dmp_DDUL8rY-bPMFJceQarS1wcqdwTalTg=s256", + id: githubBetterLineCountsId, + lastUpdated: expect.any(String), + longDescription: expect.stringContaining("Isn't it annoying when you"), + name: "GitHub: Better Line Counts", + rating: expect.any(Number), + reviewCount: expect.any(Number), + shortDescription: "Remove generated files from GitHub line counts", + storeUrl: expect.stringContaining( + "https://chromewebstore.google.com/detail/github-better-line-counts/ocfdgncpifmegplaglcnglhioflaimkd", + ), + version: expect.any(String), + weeklyActiveUsers: expect.any(Number), + screenshots: [ + { + index: 0, + indexUrl: + "http://localhost:3000/api/rest/chrome-extensions/ocfdgncpifmegplaglcnglhioflaimkd/screenshots/0", + rawUrl: expect.any(String), + }, + { + index: 1, + indexUrl: + "http://localhost:3000/api/rest/chrome-extensions/ocfdgncpifmegplaglcnglhioflaimkd/screenshots/1", + rawUrl: expect.any(String), + }, + ], + }); + }); +}); diff --git a/src/crawlers/__tests__/fixtures/chrome-web-store/.new/.keep b/src/crawlers/__tests__/fixtures/chrome-web-store/.new/.keep new file mode 100644 index 0000000..e69de29 diff --git a/src/crawlers/__tests__/fixtures/chrome-web-store/2025-02-26-kofbbilhmnkcmibjbioafflgmpkbnmme.html b/src/crawlers/__tests__/fixtures/chrome-web-store/2025-02-26-kofbbilhmnkcmibjbioafflgmpkbnmme.html new file mode 100644 index 0000000..1e7cf92 --- /dev/null +++ b/src/crawlers/__tests__/fixtures/chrome-web-store/2025-02-26-kofbbilhmnkcmibjbioafflgmpkbnmme.html @@ -0,0 +1,140 @@ +HTML to Markdown - Chrome Web Store
Item logo image for HTML to Markdown

HTML to Markdown

Item media 6 screenshot
Item video thumbnail
Item media 2 screenshot
Item media 3 screenshot
Item media 4 screenshot
Item media 5 screenshot
Item media 6 screenshot
Item video thumbnail
Item video thumbnail
Item media 2 screenshot
Item media 3 screenshot
Item media 4 screenshot
Item media 5 screenshot
Item media 6 screenshot

Overview

Convert webpages to Markdown. Easily convert headers, text, tables or code snippets to Markdown.

HTML to Markdown – a straightforward Chrome extension designed to convert most web pages into markdown format effortlessly. + +As a developer, I often found myself needing to pull high-quality documentation from websites and reformat it for use with AI chat tools. That's why I created the "HTML to Markdown" Chrome extension. This free tool quickly converts web page content into clean Markdown, making it perfect for feeding into large language models or any other AI powered chats. + +Use Cases: + +Bloggers & Content Creators: Easily grab content from articles or research pages for your own posts. + +Developers: Copy code snippets and documentation into README files, project notes, or for use with large language models. + +Students & Researchers: Quickly extract information from online resources for papers and reports. + +Technical Writers: Convert website content to create guides and documentation. + +Note Taking: Convert articles or documentation into markdown notes. + +AI Integration: Extract and convert relevant website information into markdown for input into AI powered chat tools.

0 out of 5No ratings

Google doesn't verify reviews. Learn more about results and reviews.

Details

  • Version
    0.0.13
  • Updated
    January 10, 2025
  • Offered by
    fullstackeric
  • Size
    851KiB
  • Languages
    33 languages
  • Developer
    Email
    support@fullstackeric.com
  • Non-trader
    This developer has not identified itself as a trader. For consumers in the European Union, please note that consumer rights do not apply to contracts between you and this developer.

Privacy

HTML to Markdown has disclosed the following information regarding the collection and usage of your data. More detailed information can be found in the developer's privacy policy.

HTML to Markdown handles the following:

Website content

This developer declares that your data is

  • Not being sold to third parties, outside of the approved use cases
  • Not being used or transferred for purposes that are unrelated to the item's core functionality
  • Not being used or transferred to determine creditworthiness or for lending purposes

Support

For help with questions, suggestions, or problems, visit the developer's support site

You might also like…

Page Marker - Draw on Web

4.0(543)

Draw or highlight on any website in real time. Add text, lines, and shapes. Move, undo, or redo anything you draw.

HTML to Markdown

4.5(6)

Translate a selected HTML text to equivalent markdown format

Content Edit & Blur

4.7(18)

Easily edit, replace or blur elements on any webpage

Copy as Plain Text - Format Cleaner

4.0(5)

Clear HTML formattings such as bold, italic, foreground, and background colors, then copy as plain text or replace the node

Google apps
\ No newline at end of file diff --git a/src/crawlers/__tests__/fixtures/chrome-web-store/2025-02-26-oadbjpccljkplmhnjekgjamejnbadlne.html b/src/crawlers/__tests__/fixtures/chrome-web-store/2025-02-26-oadbjpccljkplmhnjekgjamejnbadlne.html new file mode 100644 index 0000000..654680e --- /dev/null +++ b/src/crawlers/__tests__/fixtures/chrome-web-store/2025-02-26-oadbjpccljkplmhnjekgjamejnbadlne.html @@ -0,0 +1,123 @@ +Chrome Web Store
Google apps
\ No newline at end of file diff --git a/src/crawlers/__tests__/fixtures/chrome-web-store/2025-02-26-ocfdgncpifmegplaglcnglhioflaimkd.html b/src/crawlers/__tests__/fixtures/chrome-web-store/2025-02-26-ocfdgncpifmegplaglcnglhioflaimkd.html new file mode 100644 index 0000000..665a622 --- /dev/null +++ b/src/crawlers/__tests__/fixtures/chrome-web-store/2025-02-26-ocfdgncpifmegplaglcnglhioflaimkd.html @@ -0,0 +1,137 @@ +GitHub: Better Line Counts - Chrome Web Store
Item logo image for GitHub: Better Line Counts

GitHub: Better Line Counts

Featured
5.0(

4 ratings

)
Item media 1 screenshot
Item media 2 screenshot

Overview

Remove generated files from GitHub line counts

Isn't it annoying when you open a small PR, but when you look at the diff, it's +2000 -16 because you installed a new library? Or what if you had to review that PR, don't line counts like that dissuade you from starting the review? + +In reality, lots of code is generated nowadays and GitHub's line counts are not representative of a PR's true size. + +This extension subtracts generated files from the total line counts, giving you a better idea of how big a PR really is. That's it. That's all it does. + +Generated files are detected from the branch's root .gitattributes file. See GitHub's docs to learn how to mark a file as generated: https://docs.github.com/en/repositories/working-with-files/managing-files/customizing-how-changed-files-appear-on-github + +For a simple example, checkout this extension's .gitattributes file! https://github.com/aklinker1/github-better-line-counts/blob/main/.gitattributes + +--- + +The extension is open source. Feel free to contribute if you have any ideas or just star it 😀 +https://github.com/aklinker1/github-better-line-counts

5 out of 54 ratings

Google doesn't verify reviews. Learn more about results and reviews.

Details

  • Version
    1.7.1
  • Updated
    October 20, 2024
  • Offered by
    Aaron Klinker
  • Size
    123KiB
  • Languages
    English (United States)
  • Developer
    Email
    aaronklinker1@gmail.com
  • Non-trader
    This developer has not identified itself as a trader. For consumers in the European Union, please note that consumer rights do not apply to contracts between you and this developer.

Privacy

The developer has disclosed that it will not collect or use your data. To learn more, see the developer’s privacy policy.

This developer declares that your data is

  • Not being sold to third parties, outside of the approved use cases
  • Not being used or transferred for purposes that are unrelated to the item's core functionality
  • Not being used or transferred to determine creditworthiness or for lending purposes

Support

Related

Hive - Bookmarks

5.0(66)

Bookmarks with super-powers. Keyboard shortcuts, search and more!

Saverd Recipe Clipper

5.0(1)

Save recipes from hundreds of recipe sites right to your Saverd recipe box.

Agfa JIRA

3.0(2)

Adds custom actions to JIRA issue pages

GitHub File Diff

0.0(0)

View diffs of files and directories on GitHub.

HintEd Smart Manuals Editor

1.0(1)

Build an interactive onboarding and training experience for your web applications without coding.

ARGOS DOM

0.0(0)

Xpath extraction extension for ARGOS STU (HTML Action)

Michael's Upgrade for TrakED

0.0(0)

Upgraded features for TrakED in the THS. (This extension is not an official product of the THS or Intersystems TrakCare software.)

Tanuki Utilities

5.0(1)

Provide useful utilities for GitLab users.

GitHub LOC

5.0(3)

See Lines Of Code of any GitHub repository in simple yet elegant way

Left Unread

3.2(4)

Show only unread conversations on Facebook, Messenger and Twitter

Passive Aggressive Email Translator Extension

0.0(0)

Chrome extension that identifies and translates passive aggressive email in Gmail client using Gmail.JS and NodeJS-based bundling.

Thymeline

5.0(1)

Add events to your timeline the easy way

Hive - Bookmarks

5.0(66)

Bookmarks with super-powers. Keyboard shortcuts, search and more!

Saverd Recipe Clipper

5.0(1)

Save recipes from hundreds of recipe sites right to your Saverd recipe box.

Agfa JIRA

3.0(2)

Adds custom actions to JIRA issue pages

GitHub File Diff

0.0(0)

View diffs of files and directories on GitHub.

HintEd Smart Manuals Editor

1.0(1)

Build an interactive onboarding and training experience for your web applications without coding.

ARGOS DOM

0.0(0)

Xpath extraction extension for ARGOS STU (HTML Action)

Michael's Upgrade for TrakED

0.0(0)

Upgraded features for TrakED in the THS. (This extension is not an official product of the THS or Intersystems TrakCare software.)

Tanuki Utilities

5.0(1)

Provide useful utilities for GitLab users.

Google apps
\ No newline at end of file diff --git a/src/crawlers/__tests__/fixtures/chrome-web-store/2025-02-26-odffpjnpocjfcaclnenaaaddghkgijdb.html b/src/crawlers/__tests__/fixtures/chrome-web-store/2025-02-26-odffpjnpocjfcaclnenaaaddghkgijdb.html new file mode 100644 index 0000000..dce9fa5 --- /dev/null +++ b/src/crawlers/__tests__/fixtures/chrome-web-store/2025-02-26-odffpjnpocjfcaclnenaaaddghkgijdb.html @@ -0,0 +1,163 @@ +Blync: Preview Links, Selection Search and AI Sidebar & Popup - Chrome Web Store
Item logo image for Blync: Preview Links, Selection Search and AI Sidebar & Popup

Blync: Preview Links, Selection Search and AI Sidebar & Popup

Item media 6 screenshot
Item video thumbnail
Item media 2 screenshot
Item media 3 screenshot
Item media 4 screenshot
Item media 5 screenshot
Item media 6 screenshot
Item video thumbnail
Item video thumbnail
Item media 2 screenshot
Item media 3 screenshot
Item media 4 screenshot
Item media 5 screenshot
Item media 6 screenshot

Overview

Preview links, search the web without leaving your current tab and use AI (ChatGPT, Gemini) in popup and sidebar

Preview links in a popup without opening new tabs. Quickly search using text selection, select text using selection handler and search using keyboard shortcuts while staying on the same page. + +✨ Preview Links Instantly +Hover over or click on links to preview content in a popup, without navigating away from your current tab. Save time and stay focused. + +🔍 Search Smarter +Search the web directly from any page without opening a new tab. Use the selection popup or open a quick search bar using keyboard shortcut to search anything instantly. + +🤖 Turn Your AI Into an Assistant +Use your favorite AI tools like ChatGPT or Gemini automated by Blync. Set action items with prompt, on any text selection the item shows in popup and clicking on it opens AI prompt in popup or sidebar. + +👉 Is it safe to use Blync? + +Absolutely! Blync is 100% secure. + +✅ It’s serverless and runs entirely within your browser. +✅ We don’t track, store, or access your data. +✅ Everything stays private and local. + +👉 Does Blync Link Preview work on all websites? + +✅ Yes! Blync works seamlessly on any website. + +👉 Do I need an AI account to use the AI Assistant? + +✅ Yes. Blync doesn’t provide AI services. +✅ Blync automates the process, making it faster and easier for you to access AI features. + +👉 What AI agents does Blync support? + +✅ Currently, Blync supports: ChatGPT, Gemini and Claude + +💡 Why Choose Blync? + +✅ Stay Focused: No more tab overload or switching between windows. +✅ Boost Productivity: Access essential tools and information faster. +✅ Seamless Integration: Works effortlessly with the tools you already love. +✅ Whether you're working, learning or just exploring the web Blync keeps everything within reach and helps you stay in the flow. + +➡️ Install Blync now and make every click count!

5 out of 52 ratings

Google doesn't verify reviews. Learn more about results and reviews.

Details

  • Version
    0.3.3
  • Updated
    January 20, 2025
  • Size
    1.34MiB
  • Languages
    30 languages
  • Developer
    Website
    Email
    blync.app@gmail.com
  • Non-trader
    This developer has not identified itself as a trader. For consumers in the European Union, please note that consumer rights do not apply to contracts between you and this developer.

Privacy

The developer has disclosed that it will not collect or use your data. To learn more, see the developer’s privacy policy.

This developer declares that your data is

  • Not being sold to third parties, outside of the approved use cases
  • Not being used or transferred for purposes that are unrelated to the item's core functionality
  • Not being used or transferred to determine creditworthiness or for lending purposes

Support

For help with questions, suggestions, or problems, visit the developer's support site

Related

Peek Preview - Arc like link preview

5.0(1)

Peek Preview - Arc like peek preview for your web browser

Arc Peek: Link preview

4.8(6)

"Arc Peek" allows users to preview URLs, similar to the functionality in the Arc browser.

Hover

4.2(38)

Hover, preview

Link-Preview

1.8(14)

Enrich DOM links with icon, title and preview.

Link Preview Sidebar

3.9(21)

Preview links in a sidebar instead of a new tab.

Link Popper

0.0(0)

A browser extension that lets you browse linked pages without leaving the current one.

Convert To Popup

4.4(12)

Converts the current tab or link to a popup window

LightWindow: advanced link preview

3.7(3)

Link preview with nesting support and page translation. Reduce the number of open tabs.

Peek Pop

5.0(19)

An open-source add-on for previewing, searching, and collecting links to read later in a popup window.

Preview any link, anywhere - Link Preview

2.7(27)

Preview links from across the web!

Search & Link Preview

4.7(80)

See previews of links and instantly performs searches on the same page using this previewer instead of opening new tabs

Page Sidebar | Open any page in side panel

4.3(46)

Effortlessly open any website in your web browser's sidebar – streamline your workflow instantly!

Peek Preview - Arc like link preview

5.0(1)

Peek Preview - Arc like peek preview for your web browser

Arc Peek: Link preview

4.8(6)

"Arc Peek" allows users to preview URLs, similar to the functionality in the Arc browser.

Hover

4.2(38)

Hover, preview

Link-Preview

1.8(14)

Enrich DOM links with icon, title and preview.

Link Preview Sidebar

3.9(21)

Preview links in a sidebar instead of a new tab.

Link Popper

0.0(0)

A browser extension that lets you browse linked pages without leaving the current one.

Convert To Popup

4.4(12)

Converts the current tab or link to a popup window

LightWindow: advanced link preview

3.7(3)

Link preview with nesting support and page translation. Reduce the number of open tabs.

Google apps
\ No newline at end of file diff --git a/src/crawlers/chrome-crawler.ts b/src/crawlers/chrome-crawler.ts index edab813..3bd8a59 100644 --- a/src/crawlers/chrome-crawler.ts +++ b/src/crawlers/chrome-crawler.ts @@ -22,12 +22,12 @@ export async function crawlExtension( const html = await res.text(); if (!canGenerateTestFixture) { // Uncomment to debug HTML or generate new test fixture - // const date = new Date(); - // const dateString = `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`; - // Bun.write( - // `src/crawlers/__tests__/fixtures/chrome-web-store/.new/${dateString}-${id}.html`, - // html, - // ); + const date = new Date(); + const dateString = `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`; + Bun.write( + `src/crawlers/__tests__/fixtures/chrome-web-store/.new/${dateString}-${id}.html`, + html, + ); } const parsed = parseHTML(html); From 012f40d7ed1bc39665fcddff5abdafaea8dd30d7 Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 26 Feb 2025 21:21:22 -0600 Subject: [PATCH 4/4] Cleanup --- .../__snapshots__/chrome-crawler.test.ts.snap | 287 ++++++------------ 1 file changed, 94 insertions(+), 193 deletions(-) diff --git a/src/crawlers/__tests__/__snapshots__/chrome-crawler.test.ts.snap b/src/crawlers/__tests__/__snapshots__/chrome-crawler.test.ts.snap index a8212c2..6c7b6f5 100644 --- a/src/crawlers/__tests__/__snapshots__/chrome-crawler.test.ts.snap +++ b/src/crawlers/__tests__/__snapshots__/chrome-crawler.test.ts.snap @@ -1,108 +1,131 @@ // Bun Snapshot v1, https://goo.gl/fbAQLP -exports[`Chrome Web Store Crawler should extract extension details from 2025-02-26-odffpjnpocjfcaclnenaaaddghkgijdb.html 1`] = ` +exports[`Chrome Web Store Crawler should extract extension details from 2025-02-26-kofbbilhmnkcmibjbioafflgmpkbnmme.html 1`] = ` { - "iconUrl": "https://lh3.googleusercontent.com/Tr_1QA0fh5O5L5Bi3Moz6bPKxu1fC7aMrH_dCcKeLiBhjLCGmn46j59unBNxZdQ3fEymS_YXj2CHdsM-FE5vNzSEPg=s256", - "id": "odffpjnpocjfcaclnenaaaddghkgijdb", - "lastUpdated": "January 20, 2025", + "iconUrl": "https://lh3.googleusercontent.com/5JM36LnZNUMFJCJlc7Qqk8QWFYIwfqRYmAdsUhG8inAnw5LNub7ukwq37-Tw3jiDIyBbz-CAKg_ZXgERZ_gJu-0GDg=s256", + "id": "kofbbilhmnkcmibjbioafflgmpkbnmme", + "lastUpdated": "January 10, 2025", "longDescription": -"Preview links in a popup without opening new tabs. Quickly search using text selection, select text using selection handler and search using keyboard shortcuts while staying on the same page. -✨ Preview Links Instantly -Hover over or click on links to preview content in a popup, without navigating away from your current tab. Save time and stay focused. -🔍 Search Smarter -Search the web directly from any page without opening a new tab. Use the selection popup or open a quick search bar using keyboard shortcut to search anything instantly. -🤖 Turn Your AI Into an Assistant -Use your favorite AI tools like ChatGPT or Gemini automated by Blync. Set action items with prompt, on any text selection the item shows in popup and clicking on it opens AI prompt in popup or sidebar. -👉 Is it safe to use Blync? -Absolutely! Blync is 100% secure. -✅ It’s serverless and runs entirely within your browser. -✅ We don’t track, store, or access your data. -✅ Everything stays private and local. -👉 Does Blync Link Preview work on all websites? -✅ Yes! Blync works seamlessly on any website. -👉 Do I need an AI account to use the AI Assistant? -✅ Yes. Blync doesn’t provide AI services. -✅ Blync automates the process, making it faster and easier for you to access AI features. -👉 What AI agents does Blync support? -✅ Currently, Blync supports: ChatGPT, Gemini and Claude -💡 Why Choose Blync? -✅ Stay Focused: No more tab overload or switching between windows. -✅ Boost Productivity: Access essential tools and information faster. -✅ Seamless Integration: Works effortlessly with the tools you already love. -✅ Whether you're working, learning or just exploring the web Blync keeps everything within reach and helps you stay in the flow. -➡️ Install Blync now and make every click count!" +"HTML to Markdown – a straightforward Chrome extension designed to convert most web pages into markdown format effortlessly. +As a developer, I often found myself needing to pull high-quality documentation from websites and reformat it for use with AI chat tools. That's why I created the "HTML to Markdown" Chrome extension. This free tool quickly converts web page content into clean Markdown, making it perfect for feeding into large language models or any other AI powered chats. +Use Cases: +Bloggers & Content Creators: Easily grab content from articles or research pages for your own posts. +Developers: Copy code snippets and documentation into README files, project notes, or for use with large language models. +Students & Researchers: Quickly extract information from online resources for papers and reports. +Technical Writers: Convert website content to create guides and documentation. +Note Taking: Convert articles or documentation into markdown notes. +AI Integration: Extract and convert relevant website information into markdown for input into AI powered chat tools." , - "name": "Blync: Preview Links, Selection Search and AI Sidebar & Popup", - "rating": 5, - "reviewCount": 2, + "name": "HTML to Markdown", + "rating": 0, + "reviewCount": 0, "screenshots": [ { "index": 5, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/5", - "rawUrl": "https://lh3.googleusercontent.com/MXf271aHeP9deq2VjO0-_QDU-VSKgu81hcc9iKNMbAg74sI1RnvOb7Esgy95P-NTrQ7vVJtW2fMOPnTYv8pnbnMEng=s1280", + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/5", + "rawUrl": "https://lh3.googleusercontent.com/SwOe0XmN3ZkCkJQ4IF1a1i2qqCsw0b7I-Ez6VSQflAvafwiJhGbdRW6YAKNX8fAwdv159XogYSFc5Uykgt1fsYSv=s1280", }, { "index": 1, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/1", - "rawUrl": "https://lh3.googleusercontent.com/WVvtpr2Wzfr4zT34Z_Gy39ZL-I8UOZ5uReOAz-vmtC8lT4hebXlnur7y0bAWzXd6xihMbTP_PfZgQN_C4MfhixI2=s1280", + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/1", + "rawUrl": "https://lh3.googleusercontent.com/vDnkawI_rgAd5ou0gasjR5CQbm-HmbTj9SBTdlpxU_-ri1jeE9dz0qNnSOijaboiUd4tuwQBGg-ujIO-znDN4-InVZA=s1280", }, { "index": 2, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/2", - "rawUrl": "https://lh3.googleusercontent.com/e3JEIuiGu_dI70zGBPBXzL9YNZuHy8nItCoEKJSql56CcUVyIQC9Lor8HkeScWE2-qAFw5DLZQijXEfNxlInSoLl0DE=s1280", + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/2", + "rawUrl": "https://lh3.googleusercontent.com/F9H2EBxfqxrriMb0zdjoFIiCufav1oV8QI-enLqT9AEuL0nPFJLaj0286R2UH_ekxLoVa-yO76f5sSDOAjEuORrRLg=s1280", }, { "index": 3, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/3", - "rawUrl": "https://lh3.googleusercontent.com/O9OuxL28A3GOp5-R8u2kyV9wkOchMTx0ZNWraAz93gqThZRwVpCbaEW0VL1bZ0dKaKr0ReN0qJYJfSFAPOkCuOGA=s1280", + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/3", + "rawUrl": "https://lh3.googleusercontent.com/kkK_WlsYeZNGK-sU-4nvH-sEXtx7xUMuqpsK-IHq29IUkmyC3C40n1DDsDdt0f6-LgbKGL_obH_Cse-zJWJPHk-OLQ=s1280", }, { "index": 4, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/4", - "rawUrl": "https://lh3.googleusercontent.com/vfXSijB8FQ4hkARrKdJJfXe3b_ahd8yXsSuAbbKNwU9yondg0SIj1fenPJRosimBMo8bi1hj99lypD1CS1y1KhJE=s1280", + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/4", + "rawUrl": "https://lh3.googleusercontent.com/VJy9hYOMvlmwp2FhtrpIm8hNwwJVZWl-V4dZVl2cCl4aD8xNyuHaEgdm5bfjMBiZJXI0ysxul8P1GAyFaJrRvjDSuMY=s1280", }, { "index": 5, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/5", - "rawUrl": "https://lh3.googleusercontent.com/MXf271aHeP9deq2VjO0-_QDU-VSKgu81hcc9iKNMbAg74sI1RnvOb7Esgy95P-NTrQ7vVJtW2fMOPnTYv8pnbnMEng=s1280", + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/5", + "rawUrl": "https://lh3.googleusercontent.com/SwOe0XmN3ZkCkJQ4IF1a1i2qqCsw0b7I-Ez6VSQflAvafwiJhGbdRW6YAKNX8fAwdv159XogYSFc5Uykgt1fsYSv=s1280", }, { "index": 1, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/1", - "rawUrl": "https://lh3.googleusercontent.com/WVvtpr2Wzfr4zT34Z_Gy39ZL-I8UOZ5uReOAz-vmtC8lT4hebXlnur7y0bAWzXd6xihMbTP_PfZgQN_C4MfhixI2=s1280", + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/1", + "rawUrl": "https://lh3.googleusercontent.com/vDnkawI_rgAd5ou0gasjR5CQbm-HmbTj9SBTdlpxU_-ri1jeE9dz0qNnSOijaboiUd4tuwQBGg-ujIO-znDN4-InVZA=s1280", }, { "index": 2, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/2", - "rawUrl": "https://lh3.googleusercontent.com/e3JEIuiGu_dI70zGBPBXzL9YNZuHy8nItCoEKJSql56CcUVyIQC9Lor8HkeScWE2-qAFw5DLZQijXEfNxlInSoLl0DE=s1280", + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/2", + "rawUrl": "https://lh3.googleusercontent.com/F9H2EBxfqxrriMb0zdjoFIiCufav1oV8QI-enLqT9AEuL0nPFJLaj0286R2UH_ekxLoVa-yO76f5sSDOAjEuORrRLg=s1280", }, { "index": 3, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/3", - "rawUrl": "https://lh3.googleusercontent.com/O9OuxL28A3GOp5-R8u2kyV9wkOchMTx0ZNWraAz93gqThZRwVpCbaEW0VL1bZ0dKaKr0ReN0qJYJfSFAPOkCuOGA=s1280", + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/3", + "rawUrl": "https://lh3.googleusercontent.com/kkK_WlsYeZNGK-sU-4nvH-sEXtx7xUMuqpsK-IHq29IUkmyC3C40n1DDsDdt0f6-LgbKGL_obH_Cse-zJWJPHk-OLQ=s1280", }, { "index": 4, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/4", - "rawUrl": "https://lh3.googleusercontent.com/vfXSijB8FQ4hkARrKdJJfXe3b_ahd8yXsSuAbbKNwU9yondg0SIj1fenPJRosimBMo8bi1hj99lypD1CS1y1KhJE=s1280", + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/4", + "rawUrl": "https://lh3.googleusercontent.com/VJy9hYOMvlmwp2FhtrpIm8hNwwJVZWl-V4dZVl2cCl4aD8xNyuHaEgdm5bfjMBiZJXI0ysxul8P1GAyFaJrRvjDSuMY=s1280", }, { "index": 5, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/5", - "rawUrl": "https://lh3.googleusercontent.com/MXf271aHeP9deq2VjO0-_QDU-VSKgu81hcc9iKNMbAg74sI1RnvOb7Esgy95P-NTrQ7vVJtW2fMOPnTYv8pnbnMEng=s1280", + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/5", + "rawUrl": "https://lh3.googleusercontent.com/SwOe0XmN3ZkCkJQ4IF1a1i2qqCsw0b7I-Ez6VSQflAvafwiJhGbdRW6YAKNX8fAwdv159XogYSFc5Uykgt1fsYSv=s1280", }, ], - "shortDescription": "Preview links, search the web without leaving your current tab and use AI (ChatGPT, Gemini) in popup and sidebar", - "storeUrl": "https://chromewebstore.google.com/detail/blync-preview-links-selec/odffpjnpocjfcaclnenaaaddghkgijdb?hl=en", - "version": "0.3.3", - "weeklyActiveUsers": 40, + "shortDescription": "Convert webpages to Markdown. Easily convert headers, text, tables or code snippets to Markdown.", + "storeUrl": "https://chromewebstore.google.com/detail/html-to-markdown/kofbbilhmnkcmibjbioafflgmpkbnmme?hl=en", + "version": "0.0.13", + "weeklyActiveUsers": 77, +} +`; + +exports[`Chrome Web Store Crawler should extract extension details from 2025-02-26-oadbjpccljkplmhnjekgjamejnbadlne.html 1`] = `undefined`; + +exports[`Chrome Web Store Crawler should extract extension details from 2025-02-26-ocfdgncpifmegplaglcnglhioflaimkd.html 1`] = ` +{ + "iconUrl": "https://lh3.googleusercontent.com/GcffNyCJaxT2G9dsQCJHhUEMlu_E0vEzph5cLPrQj7UHKat7QyCzGu69Dmp_DDUL8rY-bPMFJceQarS1wcqdwTalTg=s256", + "id": "ocfdgncpifmegplaglcnglhioflaimkd", + "lastUpdated": "October 20, 2024", + "longDescription": +"Isn't it annoying when you open a small PR, but when you look at the diff, it's +2000 -16 because you installed a new library? Or what if you had to review that PR, don't line counts like that dissuade you from starting the review? +In reality, lots of code is generated nowadays and GitHub's line counts are not representative of a PR's true size. +This extension subtracts generated files from the total line counts, giving you a better idea of how big a PR really is. That's it. That's all it does. +Generated files are detected from the branch's root .gitattributes file. See GitHub's docs to learn how to mark a file as generated: https://docs.github.com/en/repositories/working-with-files/managing-files/customizing-how-changed-files-appear-on-github +For a simple example, checkout this extension's .gitattributes file! https://github.com/aklinker1/github-better-line-counts/blob/main/.gitattributes +--- +The extension is open source. Feel free to contribute if you have any ideas or just star it 😀 +https://github.com/aklinker1/github-better-line-counts" +, + "name": "GitHub: Better Line Counts", + "rating": 5, + "reviewCount": 4, + "screenshots": [ + { + "index": 0, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/ocfdgncpifmegplaglcnglhioflaimkd/screenshots/0", + "rawUrl": "https://lh3.googleusercontent.com/GUgh0ThX2FDPNvbaumYl4DqsUhsbYiCe-Hut9FoVEnkmTrXyA-sHbMk5jmZTj_t-dDP8rAmy6X6a6GNTCn9F8zo4VYU=s1280", + }, + { + "index": 1, + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/ocfdgncpifmegplaglcnglhioflaimkd/screenshots/1", + "rawUrl": "https://lh3.googleusercontent.com/qRi-kO0il8W6CnWa_-7oFzCwWKwr73w607I-rpYF9MM27omsuoN0k4dkgBbBECD3vZszdTSkQnoW9sywsfvAQ_7M9Q=s1280", + }, + ], + "shortDescription": "Remove generated files from GitHub line counts", + "storeUrl": "https://chromewebstore.google.com/detail/github-better-line-counts/ocfdgncpifmegplaglcnglhioflaimkd?hl=en", + "version": "1.7.1", + "weeklyActiveUsers": 251, } `; -exports[`Chrome Web Store Crawler should extract extension details from 2025-02-26-fonflmjnjbkigocpoommgmhljdpljain.html 1`] = ` +exports[`Chrome Web Store Crawler should extract extension details from 2025-02-26-odffpjnpocjfcaclnenaaaddghkgijdb.html 1`] = ` { "iconUrl": "https://lh3.googleusercontent.com/Tr_1QA0fh5O5L5Bi3Moz6bPKxu1fC7aMrH_dCcKeLiBhjLCGmn46j59unBNxZdQ3fEymS_YXj2CHdsM-FE5vNzSEPg=s256", - "id": "fonflmjnjbkigocpoommgmhljdpljain", + "id": "odffpjnpocjfcaclnenaaaddghkgijdb", "lastUpdated": "January 20, 2025", "longDescription": "Preview links in a popup without opening new tabs. Quickly search using text selection, select text using selection handler and search using keyboard shortcuts while staying on the same page. @@ -137,57 +160,57 @@ Absolutely! Blync is 100% secure. "screenshots": [ { "index": 5, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/fonflmjnjbkigocpoommgmhljdpljain/screenshots/5", + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/5", "rawUrl": "https://lh3.googleusercontent.com/MXf271aHeP9deq2VjO0-_QDU-VSKgu81hcc9iKNMbAg74sI1RnvOb7Esgy95P-NTrQ7vVJtW2fMOPnTYv8pnbnMEng=s1280", }, { "index": 1, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/fonflmjnjbkigocpoommgmhljdpljain/screenshots/1", + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/1", "rawUrl": "https://lh3.googleusercontent.com/WVvtpr2Wzfr4zT34Z_Gy39ZL-I8UOZ5uReOAz-vmtC8lT4hebXlnur7y0bAWzXd6xihMbTP_PfZgQN_C4MfhixI2=s1280", }, { "index": 2, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/fonflmjnjbkigocpoommgmhljdpljain/screenshots/2", + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/2", "rawUrl": "https://lh3.googleusercontent.com/e3JEIuiGu_dI70zGBPBXzL9YNZuHy8nItCoEKJSql56CcUVyIQC9Lor8HkeScWE2-qAFw5DLZQijXEfNxlInSoLl0DE=s1280", }, { "index": 3, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/fonflmjnjbkigocpoommgmhljdpljain/screenshots/3", + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/3", "rawUrl": "https://lh3.googleusercontent.com/O9OuxL28A3GOp5-R8u2kyV9wkOchMTx0ZNWraAz93gqThZRwVpCbaEW0VL1bZ0dKaKr0ReN0qJYJfSFAPOkCuOGA=s1280", }, { "index": 4, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/fonflmjnjbkigocpoommgmhljdpljain/screenshots/4", + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/4", "rawUrl": "https://lh3.googleusercontent.com/vfXSijB8FQ4hkARrKdJJfXe3b_ahd8yXsSuAbbKNwU9yondg0SIj1fenPJRosimBMo8bi1hj99lypD1CS1y1KhJE=s1280", }, { "index": 5, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/fonflmjnjbkigocpoommgmhljdpljain/screenshots/5", + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/5", "rawUrl": "https://lh3.googleusercontent.com/MXf271aHeP9deq2VjO0-_QDU-VSKgu81hcc9iKNMbAg74sI1RnvOb7Esgy95P-NTrQ7vVJtW2fMOPnTYv8pnbnMEng=s1280", }, { "index": 1, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/fonflmjnjbkigocpoommgmhljdpljain/screenshots/1", + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/1", "rawUrl": "https://lh3.googleusercontent.com/WVvtpr2Wzfr4zT34Z_Gy39ZL-I8UOZ5uReOAz-vmtC8lT4hebXlnur7y0bAWzXd6xihMbTP_PfZgQN_C4MfhixI2=s1280", }, { "index": 2, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/fonflmjnjbkigocpoommgmhljdpljain/screenshots/2", + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/2", "rawUrl": "https://lh3.googleusercontent.com/e3JEIuiGu_dI70zGBPBXzL9YNZuHy8nItCoEKJSql56CcUVyIQC9Lor8HkeScWE2-qAFw5DLZQijXEfNxlInSoLl0DE=s1280", }, { "index": 3, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/fonflmjnjbkigocpoommgmhljdpljain/screenshots/3", + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/3", "rawUrl": "https://lh3.googleusercontent.com/O9OuxL28A3GOp5-R8u2kyV9wkOchMTx0ZNWraAz93gqThZRwVpCbaEW0VL1bZ0dKaKr0ReN0qJYJfSFAPOkCuOGA=s1280", }, { "index": 4, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/fonflmjnjbkigocpoommgmhljdpljain/screenshots/4", + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/4", "rawUrl": "https://lh3.googleusercontent.com/vfXSijB8FQ4hkARrKdJJfXe3b_ahd8yXsSuAbbKNwU9yondg0SIj1fenPJRosimBMo8bi1hj99lypD1CS1y1KhJE=s1280", }, { "index": 5, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/fonflmjnjbkigocpoommgmhljdpljain/screenshots/5", + "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/odffpjnpocjfcaclnenaaaddghkgijdb/screenshots/5", "rawUrl": "https://lh3.googleusercontent.com/MXf271aHeP9deq2VjO0-_QDU-VSKgu81hcc9iKNMbAg74sI1RnvOb7Esgy95P-NTrQ7vVJtW2fMOPnTYv8pnbnMEng=s1280", }, ], @@ -197,125 +220,3 @@ Absolutely! Blync is 100% secure. "weeklyActiveUsers": 40, } `; - -exports[`Chrome Web Store Crawler should extract extension details from 2025-02-26-ocfdgncpifmegplaglcnglhioflaimkd.html 1`] = ` -{ - "iconUrl": "https://lh3.googleusercontent.com/GcffNyCJaxT2G9dsQCJHhUEMlu_E0vEzph5cLPrQj7UHKat7QyCzGu69Dmp_DDUL8rY-bPMFJceQarS1wcqdwTalTg=s256", - "id": "ocfdgncpifmegplaglcnglhioflaimkd", - "lastUpdated": "October 20, 2024", - "longDescription": -"Isn't it annoying when you open a small PR, but when you look at the diff, it's +2000 -16 because you installed a new library? Or what if you had to review that PR, don't line counts like that dissuade you from starting the review? -In reality, lots of code is generated nowadays and GitHub's line counts are not representative of a PR's true size. -This extension subtracts generated files from the total line counts, giving you a better idea of how big a PR really is. That's it. That's all it does. -Generated files are detected from the branch's root .gitattributes file. See GitHub's docs to learn how to mark a file as generated: https://docs.github.com/en/repositories/working-with-files/managing-files/customizing-how-changed-files-appear-on-github -For a simple example, checkout this extension's .gitattributes file! https://github.com/aklinker1/github-better-line-counts/blob/main/.gitattributes ---- -The extension is open source. Feel free to contribute if you have any ideas or just star it 😀 -https://github.com/aklinker1/github-better-line-counts" -, - "name": "GitHub: Better Line Counts", - "rating": 5, - "reviewCount": 4, - "screenshots": [ - { - "index": 0, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/ocfdgncpifmegplaglcnglhioflaimkd/screenshots/0", - "rawUrl": "https://lh3.googleusercontent.com/GUgh0ThX2FDPNvbaumYl4DqsUhsbYiCe-Hut9FoVEnkmTrXyA-sHbMk5jmZTj_t-dDP8rAmy6X6a6GNTCn9F8zo4VYU=s1280", - }, - { - "index": 1, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/ocfdgncpifmegplaglcnglhioflaimkd/screenshots/1", - "rawUrl": "https://lh3.googleusercontent.com/qRi-kO0il8W6CnWa_-7oFzCwWKwr73w607I-rpYF9MM27omsuoN0k4dkgBbBECD3vZszdTSkQnoW9sywsfvAQ_7M9Q=s1280", - }, - ], - "shortDescription": "Remove generated files from GitHub line counts", - "storeUrl": "https://chromewebstore.google.com/detail/github-better-line-counts/ocfdgncpifmegplaglcnglhioflaimkd?hl=en", - "version": "1.7.1", - "weeklyActiveUsers": 251, -} -`; - -exports[`Chrome Web Store Crawler should extract extension details from 2025-02-26-kofbbilhmnkcmibjbioafflgmpkbnmme.html 1`] = ` -{ - "iconUrl": "https://lh3.googleusercontent.com/5JM36LnZNUMFJCJlc7Qqk8QWFYIwfqRYmAdsUhG8inAnw5LNub7ukwq37-Tw3jiDIyBbz-CAKg_ZXgERZ_gJu-0GDg=s256", - "id": "kofbbilhmnkcmibjbioafflgmpkbnmme", - "lastUpdated": "January 10, 2025", - "longDescription": -"HTML to Markdown – a straightforward Chrome extension designed to convert most web pages into markdown format effortlessly. -As a developer, I often found myself needing to pull high-quality documentation from websites and reformat it for use with AI chat tools. That's why I created the "HTML to Markdown" Chrome extension. This free tool quickly converts web page content into clean Markdown, making it perfect for feeding into large language models or any other AI powered chats. -Use Cases: -Bloggers & Content Creators: Easily grab content from articles or research pages for your own posts. -Developers: Copy code snippets and documentation into README files, project notes, or for use with large language models. -Students & Researchers: Quickly extract information from online resources for papers and reports. -Technical Writers: Convert website content to create guides and documentation. -Note Taking: Convert articles or documentation into markdown notes. -AI Integration: Extract and convert relevant website information into markdown for input into AI powered chat tools." -, - "name": "HTML to Markdown", - "rating": 0, - "reviewCount": 0, - "screenshots": [ - { - "index": 5, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/5", - "rawUrl": "https://lh3.googleusercontent.com/SwOe0XmN3ZkCkJQ4IF1a1i2qqCsw0b7I-Ez6VSQflAvafwiJhGbdRW6YAKNX8fAwdv159XogYSFc5Uykgt1fsYSv=s1280", - }, - { - "index": 1, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/1", - "rawUrl": "https://lh3.googleusercontent.com/vDnkawI_rgAd5ou0gasjR5CQbm-HmbTj9SBTdlpxU_-ri1jeE9dz0qNnSOijaboiUd4tuwQBGg-ujIO-znDN4-InVZA=s1280", - }, - { - "index": 2, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/2", - "rawUrl": "https://lh3.googleusercontent.com/F9H2EBxfqxrriMb0zdjoFIiCufav1oV8QI-enLqT9AEuL0nPFJLaj0286R2UH_ekxLoVa-yO76f5sSDOAjEuORrRLg=s1280", - }, - { - "index": 3, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/3", - "rawUrl": "https://lh3.googleusercontent.com/kkK_WlsYeZNGK-sU-4nvH-sEXtx7xUMuqpsK-IHq29IUkmyC3C40n1DDsDdt0f6-LgbKGL_obH_Cse-zJWJPHk-OLQ=s1280", - }, - { - "index": 4, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/4", - "rawUrl": "https://lh3.googleusercontent.com/VJy9hYOMvlmwp2FhtrpIm8hNwwJVZWl-V4dZVl2cCl4aD8xNyuHaEgdm5bfjMBiZJXI0ysxul8P1GAyFaJrRvjDSuMY=s1280", - }, - { - "index": 5, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/5", - "rawUrl": "https://lh3.googleusercontent.com/SwOe0XmN3ZkCkJQ4IF1a1i2qqCsw0b7I-Ez6VSQflAvafwiJhGbdRW6YAKNX8fAwdv159XogYSFc5Uykgt1fsYSv=s1280", - }, - { - "index": 1, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/1", - "rawUrl": "https://lh3.googleusercontent.com/vDnkawI_rgAd5ou0gasjR5CQbm-HmbTj9SBTdlpxU_-ri1jeE9dz0qNnSOijaboiUd4tuwQBGg-ujIO-znDN4-InVZA=s1280", - }, - { - "index": 2, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/2", - "rawUrl": "https://lh3.googleusercontent.com/F9H2EBxfqxrriMb0zdjoFIiCufav1oV8QI-enLqT9AEuL0nPFJLaj0286R2UH_ekxLoVa-yO76f5sSDOAjEuORrRLg=s1280", - }, - { - "index": 3, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/3", - "rawUrl": "https://lh3.googleusercontent.com/kkK_WlsYeZNGK-sU-4nvH-sEXtx7xUMuqpsK-IHq29IUkmyC3C40n1DDsDdt0f6-LgbKGL_obH_Cse-zJWJPHk-OLQ=s1280", - }, - { - "index": 4, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/4", - "rawUrl": "https://lh3.googleusercontent.com/VJy9hYOMvlmwp2FhtrpIm8hNwwJVZWl-V4dZVl2cCl4aD8xNyuHaEgdm5bfjMBiZJXI0ysxul8P1GAyFaJrRvjDSuMY=s1280", - }, - { - "index": 5, - "indexUrl": "http://localhost:3000/api/rest/chrome-extensions/kofbbilhmnkcmibjbioafflgmpkbnmme/screenshots/5", - "rawUrl": "https://lh3.googleusercontent.com/SwOe0XmN3ZkCkJQ4IF1a1i2qqCsw0b7I-Ez6VSQflAvafwiJhGbdRW6YAKNX8fAwdv159XogYSFc5Uykgt1fsYSv=s1280", - }, - ], - "shortDescription": "Convert webpages to Markdown. Easily convert headers, text, tables or code snippets to Markdown.", - "storeUrl": "https://chromewebstore.google.com/detail/html-to-markdown/kofbbilhmnkcmibjbioafflgmpkbnmme?hl=en", - "version": "0.0.13", - "weeklyActiveUsers": 77, -} -`; - -exports[`Chrome Web Store Crawler should extract extension details from 2025-02-26-oadbjpccljkplmhnjekgjamejnbadlne.html 1`] = `undefined`;