From 73f999775ff803495d512ce85a692ad94ce89343 Mon Sep 17 00:00:00 2001 From: devartes Date: Wed, 17 Jul 2024 17:18:39 -0300 Subject: [PATCH 1/4] feat: add script tailwind checker --- scripts/tailwind_checker.ts | 120 ++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 scripts/tailwind_checker.ts diff --git a/scripts/tailwind_checker.ts b/scripts/tailwind_checker.ts new file mode 100644 index 000000000..5ab3438b7 --- /dev/null +++ b/scripts/tailwind_checker.ts @@ -0,0 +1,120 @@ +//Tailwind Checker by Carcará +import { walk } from "https://deno.land/std@0.201.0/fs/mod.ts"; +import * as colors from "std/fmt/colors.ts"; + +const folderPaths = ["./components", "./sections"]; +const tailwindCssUrl = + "https://cdn.jsdelivr.net/npm/tailwindcss@2.2.15/dist/tailwind.min.css"; + +async function getAllClassesInCss(url: string): Promise { + try { + const response = await fetch(url); + if (!response.ok) { + throw new Error( + `Failed to fetch ${url}: ${response.status} ${response.statusText}`, + ); + } + const cssText = await response.text(); + + const regexPattern = /\.([a-zA-Z0-9_\-\\]+)/g; + const matches = cssText.match(regexPattern); + + return matches ? matches.map((cls) => cls.replace(".", "")) : []; + } catch (error) { + console.error(`Error fetching or processing CSS file: ${error}`); + return []; + } +} + +async function getAllClassesInCode( + folderPaths: string[], + cssClasses: string[], +): Promise<{ file: string; class: string }[]> { + const allClasses: { file: string; class: string }[] = []; + + for (const folderPath of folderPaths) { + for await (const entry of walk(folderPath, { exts: [".tsx"] })) { + if (entry.isFile) { + try { + const data = await Deno.readTextFile(entry.path); + const lines = data.split("\n"); + const classes = data.match(/class\s*=\s*["'`]([^"'`]+)["'`]/g); + + if (classes) { + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const classList = line.match(/class\s*=\s*["'`]([^"'`]+)["'`]/); + if (classList && classList[1]) { + const individualClasses = classList[1].split(" "); + individualClasses.forEach((cls) => { + const cleanCls = cls.replace(/\\/g, ""); + if ( + !cssClasses.includes(cleanCls) && /\-[\[\]]/.test(cleanCls) + ) { + allClasses.push({ + file: `${entry.path}:${i + 1}`, + class: cleanCls, + }); + } + }); + } + } + } + } catch (err) { + console.error(`Error processing file ${entry.path}: ${err}`); + } + } + } + } + + return allClasses; +} + +function showLoadingAnimation() { + const loadingAnimation = ["|", "/", "-", "\\"]; + let index = 0; + + const interval = setInterval(() => { + Deno.stdout.write( + new TextEncoder().encode(`\rDynamic Classes: ${loadingAnimation[index]}`), + ); + index = (index + 1) % loadingAnimation.length; + }, 100); + + return interval; +} + +async function compareClasses() { + const interval = showLoadingAnimation(); + + try { + const cssClasses = await getAllClassesInCss(tailwindCssUrl); + const projectClasses = await getAllClassesInCode(folderPaths, cssClasses); + + clearInterval(interval); + + if (cssClasses.length > 0 && projectClasses.length > 0) { + const differentClasses = projectClasses.filter((entry) => + !cssClasses.includes(entry.class) + ); + + if (differentClasses.length > 0) { + console.log("\n"); + console.log( + colors.green( + `✨ Hint: Click on the file paths to jump to the location of the class`, + ), + ); + console.table(differentClasses, ["file", "class"]); + } else { + console.log("All classes found in project files are also in CSS."); + } + } else { + console.log("No classes found in CSS or project files."); + } + } catch (error) { + console.error(`Error comparing classes: ${error}`); + } +} + +compareClasses(); From 7fe732805822f988dfe7d98a4a4645637f1af831 Mon Sep 17 00:00:00 2001 From: devartes Date: Thu, 15 Aug 2024 08:27:21 -0300 Subject: [PATCH 2/4] refactor: improving identification of dynamic classes --- scripts/tailwind_checker.ts | 75 +++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/scripts/tailwind_checker.ts b/scripts/tailwind_checker.ts index 5ab3438b7..f3220c2e6 100644 --- a/scripts/tailwind_checker.ts +++ b/scripts/tailwind_checker.ts @@ -1,11 +1,17 @@ -//Tailwind Checker by Carcará import { walk } from "https://deno.land/std@0.201.0/fs/mod.ts"; -import * as colors from "std/fmt/colors.ts"; -const folderPaths = ["./components", "./sections"]; +const green = "\x1b[32m"; +const reset = "\x1b[0m"; + +const folderPaths = ["./components", "./sections", "./islands"]; const tailwindCssUrl = "https://cdn.jsdelivr.net/npm/tailwindcss@2.2.15/dist/tailwind.min.css"; +function isDynamicClass(cls: string): boolean { + const bracketValueRegex = /\[\d+(px|em|rem|%|vh|vw|vmin|vmax|ch|ex)\]/; + return bracketValueRegex.test(cls); +} + async function getAllClassesInCss(url: string): Promise { try { const response = await fetch(url); @@ -33,37 +39,44 @@ async function getAllClassesInCode( const allClasses: { file: string; class: string }[] = []; for (const folderPath of folderPaths) { - for await (const entry of walk(folderPath, { exts: [".tsx"] })) { - if (entry.isFile) { - try { - const data = await Deno.readTextFile(entry.path); - const lines = data.split("\n"); - const classes = data.match(/class\s*=\s*["'`]([^"'`]+)["'`]/g); - - if (classes) { - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - const classList = line.match(/class\s*=\s*["'`]([^"'`]+)["'`]/); - if (classList && classList[1]) { - const individualClasses = classList[1].split(" "); - individualClasses.forEach((cls) => { - const cleanCls = cls.replace(/\\/g, ""); - if ( - !cssClasses.includes(cleanCls) && /\-[\[\]]/.test(cleanCls) - ) { - allClasses.push({ - file: `${entry.path}:${i + 1}`, - class: cleanCls, - }); - } - }); + try { + for await (const entry of walk(folderPath, { exts: [".tsx"] })) { + if (entry.isFile) { + try { + const data = await Deno.readTextFile(entry.path); + const lines = data.split("\n"); + const classes = data.match(/class\s*=\s*["'`]([^"'`]+)["'`]/g); + + if (classes) { + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const classList = line.match(/class\s*=\s*["'`]([^"'`]+)["'`]/); + if (classList && classList[1]) { + const individualClasses = classList[1].split(" "); + individualClasses.forEach((cls) => { + const cleanCls = cls.replace(/\\/g, ""); + if ( + !cssClasses.includes(cleanCls) && + isDynamicClass(cleanCls) + ) { + allClasses.push({ + file: `${entry.path}:${i + 1}`, + class: cleanCls, + }); + } + }); + } } } + } catch (err) { + console.error(`Error processing file ${entry.path}: ${err}`); } - } catch (err) { - console.error(`Error processing file ${entry.path}: ${err}`); } } + } catch (err) { + if (err instanceof Deno.errors.NotFound) { + continue; + } } } @@ -101,9 +114,7 @@ async function compareClasses() { if (differentClasses.length > 0) { console.log("\n"); console.log( - colors.green( - `✨ Hint: Click on the file paths to jump to the location of the class`, - ), + `${green}✨ Hint: Click on the file paths to jump to the location of the class${reset}`, ); console.table(differentClasses, ["file", "class"]); } else { From f6db15f6be56f8027aff7bf3cdb26c21e5d11226 Mon Sep 17 00:00:00 2001 From: devartes Date: Tue, 20 Aug 2024 08:50:47 -0300 Subject: [PATCH 3/4] refactor: updating code as per review --- scripts/deno.json | 3 ++- scripts/tailwind_checker.ts | 30 +++++++++++++++++++----------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/scripts/deno.json b/scripts/deno.json index cbcaab834..49b7d5a47 100644 --- a/scripts/deno.json +++ b/scripts/deno.json @@ -4,7 +4,8 @@ "exports": { "./release": "./release.ts", "./update": "./update.run.ts", - "./upgrade": "./upgrade.ts" + "./upgrade": "./upgrade.ts", + "./tailwind_checker.ts": "./tailwind_checker.ts" }, "publish": { "exclude": [ diff --git a/scripts/tailwind_checker.ts b/scripts/tailwind_checker.ts index f3220c2e6..c53b71454 100644 --- a/scripts/tailwind_checker.ts +++ b/scripts/tailwind_checker.ts @@ -1,9 +1,7 @@ -import { walk } from "https://deno.land/std@0.201.0/fs/mod.ts"; +import { walk } from "@std/fs/walk"; +import * as colors from "@std/fmt/colors"; -const green = "\x1b[32m"; -const reset = "\x1b[0m"; - -const folderPaths = ["./components", "./sections", "./islands"]; +const folderPaths = "./*"; const tailwindCssUrl = "https://cdn.jsdelivr.net/npm/tailwindcss@2.2.15/dist/tailwind.min.css"; @@ -21,7 +19,6 @@ async function getAllClassesInCss(url: string): Promise { ); } const cssText = await response.text(); - const regexPattern = /\.([a-zA-Z0-9_\-\\]+)/g; const matches = cssText.match(regexPattern); @@ -33,15 +30,22 @@ async function getAllClassesInCss(url: string): Promise { } async function getAllClassesInCode( - folderPaths: string[], + folderPaths: string, cssClasses: string[], ): Promise<{ file: string; class: string }[]> { const allClasses: { file: string; class: string }[] = []; for (const folderPath of folderPaths) { try { - for await (const entry of walk(folderPath, { exts: [".tsx"] })) { - if (entry.isFile) { + for await (const entry of walk(folderPath)) { + if (entry.isFile && entry.path.endsWith(".tsx")) { + const ignorePaths = ["node_modules", ".gitignore"]; + if ( + ignorePaths.some((ignorePath) => entry.path.includes(ignorePath)) + ) { + continue; + } + try { const data = await Deno.readTextFile(entry.path); const lines = data.split("\n"); @@ -114,7 +118,9 @@ async function compareClasses() { if (differentClasses.length > 0) { console.log("\n"); console.log( - `${green}✨ Hint: Click on the file paths to jump to the location of the class${reset}`, + colors.green( + `✨ Hint: Click on the file paths to jump to the location of the class`, + ), ); console.table(differentClasses, ["file", "class"]); } else { @@ -128,4 +134,6 @@ async function compareClasses() { } } -compareClasses(); +if (import.meta.main) { + await compareClasses(); +} From 3df4e52712cbd21970cba3ec0bcc749cdf85d314 Mon Sep 17 00:00:00 2001 From: devartes Date: Thu, 22 Aug 2024 10:27:24 -0300 Subject: [PATCH 4/4] chore: rename file --- scripts/deno.json | 2 +- scripts/{tailwind_checker.ts => tailwindChecker.ts} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename scripts/{tailwind_checker.ts => tailwindChecker.ts} (100%) diff --git a/scripts/deno.json b/scripts/deno.json index 49b7d5a47..4cf3c531f 100644 --- a/scripts/deno.json +++ b/scripts/deno.json @@ -5,7 +5,7 @@ "./release": "./release.ts", "./update": "./update.run.ts", "./upgrade": "./upgrade.ts", - "./tailwind_checker.ts": "./tailwind_checker.ts" + "./tailwindChecker.ts": "./tailwindChecker.ts" }, "publish": { "exclude": [ diff --git a/scripts/tailwind_checker.ts b/scripts/tailwindChecker.ts similarity index 100% rename from scripts/tailwind_checker.ts rename to scripts/tailwindChecker.ts