Skip to content

Commit 400c96c

Browse files
authored
Merge pull request #1 from dankore/new-new-new
New look and smaller package size
2 parents ef1cfe3 + 8a4fe34 commit 400c96c

File tree

13 files changed

+354
-951
lines changed

13 files changed

+354
-951
lines changed

.github/workflows/release.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ jobs:
1010
runs-on: ubuntu-latest
1111
steps:
1212
- name: Check out the repo
13-
uses: actions/checkout@v2
13+
uses: actions/checkout@v4
1414

1515
- name: Actual Release
1616
uses: 'marvinpinto/action-automatic-releases@latest'
1717
with:
1818
repo_token: ${{ secrets.CR_PAT_BUILT_WITH_TAILWIND_CSS }}
19-
automatic_release_tag: 1.2.0
19+
automatic_release_tag: 2.0.0
2020
draft: false
2121
prerelease: false
2222
files: built-with-tailwind-css.zip

built-with-tailwind-css.zip

-28.4 KB
Binary file not shown.

css/input.css

Lines changed: 0 additions & 3 deletions
This file was deleted.

package.json

Lines changed: 0 additions & 20 deletions
This file was deleted.

src/background.js

Lines changed: 145 additions & 181 deletions
Original file line numberDiff line numberDiff line change
@@ -1,192 +1,156 @@
1-
'use strict';
2-
3-
importScripts('/utils.js');
4-
5-
const { getFromStorage, setToStorage, getCurrentTabRootDomainFromStorage, getUrlProtocolPlusHostname } = Utils;
6-
7-
const Engine = {
8-
storageCache: {},
9-
/**
10-
* Initialize Engine
11-
*/
12-
async init() {
13-
// get all stored domains and their values from local storage
14-
Engine.storageCache = await Engine.getAllStorageSyncData();
15-
16-
// on tab change, get the current tab's root domain and check if it has Tailwind CSS
17-
this.performOnTabChange();
18-
19-
// create alarm to delete local storage and cache
20-
Engine.createAlarm();
21-
22-
// delete local storage and cache after a week
23-
Engine.reset('reset');
24-
25-
chrome.webRequest.onCompleted.addListener(Engine.onStyleSheetRequestComplete, {
26-
urls: ['http://*/*', 'https://*/*'],
27-
types: ['stylesheet'],
28-
});
29-
},
30-
31-
disAllowedList: ['https://chrome.google.com', 'https://chrome.google.com/'],
32-
33-
/**
34-
* Create an alarm to delete local storage and cache
35-
*/
36-
createAlarm() {
37-
chrome.alarms.create('reset', {
38-
// get week in minutes
39-
periodInMinutes: 60 * 24 * 7,
40-
});
41-
42-
console.log('Reset alarm set to occur every week');
43-
},
44-
45-
/**
46-
* Listen for alarm and act accordingly
47-
*/
48-
reset(alarmName) {
49-
chrome.alarms.onAlarm.addListener(async alarm => {
50-
if (alarm.name === alarmName) {
51-
try {
52-
console.log(alarmName, 'Resetting...');
53-
Engine.resetCacheAndLocalStorage()
54-
.then(() => {
55-
console.log('storage after reset', Engine.storageCache);
56-
})
57-
.catch(error => console.error('reset', error));
58-
} catch (error) {
59-
console.error('reset', error);
60-
}
61-
}
62-
});
63-
},
64-
65-
/**
66-
* Clear caches
67-
*/
68-
async resetCacheAndLocalStorage() {
69-
Engine.storageCache = {};
70-
await Utils.promisify(chrome.storage.sync, 'clear');
71-
},
1+
// background.js
2+
3+
// Cache for Tailwind detection results (domain -> { hasTailwindCSS, tailwindVersion })
4+
const domainCache = {};
5+
6+
// Helper functions
7+
const getDomain = (url) => {
8+
try {
9+
const { hostname } = new URL(url);
10+
return hostname;
11+
} catch (error) {
12+
console.error("Error parsing URL:", error);
13+
return null;
14+
}
15+
};
7216

73-
/**
74-
* Get all storage data
75-
* @returns {Promise<any>}
76-
* @private
77-
* @see https://developer.chrome.com/extensions/storage#type-StorageArea
78-
* @see https://developer.chrome.com/extensions/storage#method-StorageArea.get
79-
* @see https://developer.chrome.com/extensions/storage#type-StorageArea.StorageArea
80-
* */
81-
async getAllStorageSyncData() {
82-
// Immediately return a promise and start asynchronous work
83-
return new Promise((resolve, reject) => {
84-
// Asynchronously fetch all data from storage.sync.
85-
chrome.storage.sync.get(null, items => {
86-
// Pass any observed errors down the promise chain.
87-
if (chrome.runtime.lastError) {
88-
return reject(chrome.runtime.lastError);
89-
}
90-
// Pass the data retrieved from storage down the promise chain.
91-
resolve(items);
92-
});
93-
});
94-
},
17+
const updateCacheAndBadge = (domain, hasTailwindCSS, tailwindVersion) => {
18+
domainCache[domain] = { hasTailwindCSS, tailwindVersion };
19+
console.log(`Updated cache for ${domain}: ${hasTailwindCSS}, version: ${tailwindVersion}`);
20+
updateBadge(hasTailwindCSS, tailwindVersion);
21+
};
9522

96-
/**
97-
* Perform action on tab change
98-
*/
99-
performOnTabChange() {
100-
chrome.tabs.onActivated.addListener(async () => {
101-
getCurrentTabRootDomainFromStorage().then(({ storage, rootDomain }) => {
102-
// check cache for root domain
103-
if (!Engine.storageCache[rootDomain] && Engine.beginsWithHttpProtocols(rootDomain) && !Engine.disAllowedList.includes(rootDomain)) {
104-
fetch(rootDomain)
105-
.then(async function (response) {
106-
// regex get stylesheet
107-
const regex = /<link[^>]*rel="stylesheet"[^>]*href="([^"]*)"[^>]*>/g;
23+
const updateBadge = (hasTailwindCSS, tailwindVersion = "unknown") => {
24+
const badgeText = hasTailwindCSS
25+
? tailwindVersion === "unknown"
26+
? "UN"
27+
: `T${tailwindVersion.split(".")[0]}`
28+
: "";
29+
chrome.action.setBadgeText({ text: badgeText });
30+
chrome.action.setTitle({
31+
title: hasTailwindCSS
32+
? `Tailwind CSS v${tailwindVersion}`
33+
: "This website is not using Tailwind CSS",
34+
});
35+
};
10836

109-
const links = [];
110-
let match;
111-
const text = await response.text();
37+
const clearBadge = () => {
38+
chrome.action.setBadgeText({ text: "" });
39+
chrome.action.setTitle({ title: "Tailwind CSS Detector" });
40+
};
11241

113-
while ((match = regex.exec(text)) !== null) {
114-
links.push(match[1]);
115-
}
42+
const resetCache = () => {
43+
Object.keys(domainCache).forEach((domain) => {
44+
delete domainCache[domain];
45+
});
46+
console.log("Cache reset successfully via scheduled alarm");
47+
clearBadge();
48+
};
11649

117-
if (links.length > 0) {
118-
for await (const link of links) {
119-
if (Engine.beginsWithHttpProtocols(link) && !Engine.disAllowedList.includes(link)) {
120-
await Engine.fetchStyleSheet(link, rootDomain);
121-
} else {
122-
await Engine.fetchStyleSheet(`${rootDomain}${link}`, rootDomain);
123-
}
124-
}
50+
const evaluateTab = (tabId) => {
51+
chrome.tabs.get(tabId, (tab) => {
52+
if (
53+
tab &&
54+
tab.url &&
55+
!tab.url.startsWith("chrome://") &&
56+
tab.url !== "about:blank" &&
57+
tab.url !== "chrome://newtab/"
58+
) {
59+
const domain = getDomain(tab.url);
60+
if (domain && domainCache[domain]) {
61+
console.log(`Cache hit: ${domain}`);
62+
updateBadge(domainCache[domain].hasTailwindCSS, domainCache[domain].tailwindVersion);
63+
} else {
64+
console.log(`Cache miss: ${domain}`);
65+
chrome.scripting.executeScript(
66+
{
67+
target: { tabId },
68+
files: ["content.js"],
69+
},
70+
() => {
71+
chrome.tabs.sendMessage(tabId, { action: "checkForTailwindCSS" }, (response) => {
72+
if (chrome.runtime.lastError) {
73+
console.error("Error sending message to tab:", chrome.runtime.lastError.message);
74+
clearBadge();
12575
} else {
126-
// no stylesheet found
127-
console.log('no stylesheet found for root domain -', rootDomain);
128-
129-
await setToStorage(rootDomain, {});
130-
Engine.storageCache[rootDomain] = {};
76+
console.log(`Response from content script for ${domain}:`, response);
77+
if (response && typeof response.hasTailwindCSS !== "undefined") {
78+
updateCacheAndBadge(domain, response.hasTailwindCSS, response.tailwindVersion);
79+
}
13180
}
132-
})
133-
.catch(error => console.log('fetch error', error));
134-
} else {
135-
console.log('cache hit', rootDomain);
136-
}
137-
});
138-
});
139-
},
140-
141-
/**
142-
* Check if string is starts with https:// or http:// or www.
143-
* @param {String} url
144-
* */
145-
beginsWithHttpProtocols(url) {
146-
if (url) {
147-
return url.startsWith('https://') || url.startsWith('http://') || url.startsWith('www.');
148-
}
149-
},
150-
151-
async fetchStyleSheet(url, rootDomain) {
152-
fetch(url)
153-
.then(response => response.text())
154-
.then(async text => {
155-
await Engine.analyze(text, rootDomain);
156-
})
157-
.catch(error => console.log(rootDomain, error));
158-
},
159-
160-
/**
161-
* analyze stylesheets
162-
* @param {Object} request
163-
*/
164-
async onStyleSheetRequestComplete(request) {
165-
await Engine.fetchStyleSheet(request.url, getUrlProtocolPlusHostname(request.initiator));
166-
},
167-
168-
async analyze(text, rootDomain) {
169-
if (rootDomain) {
170-
// detect tailwindcss
171-
const regexHasTailwindcss = /(?<![\w\d])(?:tailwind|tailwindcss|--tw-bg-opacity|--tw-hue-rotate|--tw-translate-x|--tw-ring-offset-width|--tw-ring-shadow|--tw-content)(?![\w\d])/gi;
172-
const hasTailwindCss = regexHasTailwindcss.test(text);
173-
174-
// detect tailwindcss version
175-
const regexHasVersion = /(?:^|\s)tailwindcss\s+([^\s]+)/gi;
176-
const versions = [];
177-
178-
let match;
179-
180-
while ((match = regexHasVersion.exec(text))) {
181-
versions.push(match[1]);
81+
});
82+
}
83+
);
18284
}
183-
184-
await setToStorage(rootDomain, { versions, hasTailwindCss });
185-
186-
// Cache the data
187-
Engine.storageCache[rootDomain] = { versions, hasTailwindCss };
85+
} else {
86+
clearBadge();
87+
console.log(`Skipping tab with URL: ${tab ? tab.url : "unknown"}`);
18888
}
189-
},
89+
});
19090
};
19191

192-
Engine.init();
92+
// Event listeners
93+
chrome.tabs.onActivated.addListener(({ tabId }) => evaluateTab(tabId));
94+
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
95+
if (changeInfo.status === "complete") {
96+
evaluateTab(tabId);
97+
}
98+
});
99+
chrome.tabs.onRemoved.addListener(clearBadge);
100+
101+
// Create an alarm to reset the cache every two weeks
102+
chrome.alarms.create("resetCacheAlarm", { periodInMinutes: 20160 });
103+
104+
// Listen for the cache reset alarm
105+
chrome.alarms.onAlarm.addListener((alarm) => {
106+
if (alarm.name === "resetCacheAlarm") {
107+
resetCache();
108+
}
109+
});
110+
111+
// Listen for messages from the popup
112+
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
113+
console.log("Message received in background script:", message);
114+
115+
if (message.requestUpdate) {
116+
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
117+
if (tabs.length > 0) {
118+
const tabId = tabs[0].id;
119+
chrome.tabs.get(tabId, (tab) => {
120+
if (tab && tab.url) {
121+
const domain = getDomain(tab.url);
122+
if (domain && domainCache[domain]) {
123+
console.log(`Popup Cache hit: ${domain}`);
124+
sendResponse(domainCache[domain]);
125+
} else {
126+
console.log(`Popup Cache miss: ${domain}`);
127+
evaluateTab(tabId);
128+
sendResponse({ hasTailwindCSS: false, tailwindVersion: "unknown" });
129+
}
130+
}
131+
});
132+
} else {
133+
console.log("No active tab available");
134+
}
135+
});
136+
return true; // To allow asynchronous `sendResponse`
137+
}
138+
139+
if (typeof message.hasTailwindCSS !== "undefined") {
140+
const tabId = sender.tab?.id;
141+
if (tabId) {
142+
chrome.tabs.get(tabId, (tab) => {
143+
if (tab && tab.url) {
144+
const domain = getDomain(tab.url);
145+
if (domain) {
146+
updateCacheAndBadge(domain, message.hasTailwindCSS, message.tailwindVersion);
147+
}
148+
}
149+
});
150+
}
151+
} else {
152+
console.log("Invalid message received");
153+
}
154+
sendResponse({ status: "done" });
155+
return true; // Ensure the sendResponse is maintained
156+
});

0 commit comments

Comments
 (0)