mirror of
https://github.com/Lissy93/web-check.git
synced 2026-05-12 21:00:38 -04:00
feat: Removed carbon dep, remove partytown, open links in new tab
This commit is contained in:
@@ -16,6 +16,18 @@ const GRID_INTENSITY = 442;
|
||||
const RENEWABLE_INTENSITY = 50;
|
||||
const LITRES_PER_GRAM = 0.5562;
|
||||
|
||||
// Reference median grams CO2 per visit, drawn from websitecarbon's published average.
|
||||
// Used to estimate a percentile rank since we lack their measured-sites dataset
|
||||
const REFERENCE_MEDIAN_GRAMS = 0.8;
|
||||
|
||||
// Approximate percentile via log2 distance from the reference median.
|
||||
// 1 doubling above median drops 25 points; clamp to [1, 99]
|
||||
const estimateCleanerThan = (grams) => {
|
||||
if (!grams || grams <= 0) return 0;
|
||||
const pct = 50 - 25 * Math.log2(grams / REFERENCE_MEDIAN_GRAMS);
|
||||
return Math.max(1, Math.min(99, Math.round(pct)));
|
||||
};
|
||||
|
||||
// Stream the response, cap at MAX_BYTES so huge pages can't blow memory or time
|
||||
const fetchByteCount = async (url) => {
|
||||
const r = await fetch(url, {
|
||||
@@ -66,11 +78,13 @@ const carbonHandler = async (url) => {
|
||||
}
|
||||
if (!bytes) return { skipped: 'Site returned no content, cannot calculate carbon' };
|
||||
log.debug(`measured ${bytes} bytes for ${url}`);
|
||||
const statistics = computeCarbon(bytes);
|
||||
return {
|
||||
url,
|
||||
bytes,
|
||||
green: false,
|
||||
statistics: computeCarbon(bytes),
|
||||
statistics,
|
||||
cleanerThan: estimateCleanerThan(statistics.co2.grid.grams),
|
||||
scanUrl: url,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -4,7 +4,6 @@ import { loadEnv } from 'vite';
|
||||
// Integrations
|
||||
import svelte from '@astrojs/svelte';
|
||||
import react from '@astrojs/react';
|
||||
import partytown from '@astrojs/partytown';
|
||||
import sitemap from '@astrojs/sitemap';
|
||||
|
||||
// Adapters
|
||||
@@ -36,7 +35,7 @@ const base = unwrapEnvVar('BASE_URL', '/');
|
||||
const isBossServer = unwrapEnvVar('BOSS_SERVER', false);
|
||||
|
||||
// Initialize Astro integrations
|
||||
const integrations = [svelte(), react(), partytown(), sitemap()];
|
||||
const integrations = [svelte(), react(), sitemap()];
|
||||
|
||||
// Set the appropriate adapter, based on the deploy target
|
||||
function getAdapter(target) {
|
||||
|
||||
@@ -73,7 +73,6 @@
|
||||
"@astrojs/cloudflare": "^13.3.1",
|
||||
"@astrojs/netlify": "^7.0.8",
|
||||
"@astrojs/node": "^10.0.6",
|
||||
"@astrojs/partytown": "^2.1.7",
|
||||
"@astrojs/sitemap": "^3.7.2",
|
||||
"@astrojs/svelte": "^8.1.0",
|
||||
"@astrojs/ts-plugin": "^1.10.7",
|
||||
|
||||
@@ -219,7 +219,7 @@ const Row = (props: RowProps) => {
|
||||
{lbl}
|
||||
</span>
|
||||
)}
|
||||
<span className="val" title={val} onClick={() => copyToClipboard(val)}>
|
||||
<span className="val" title={val?.toString()} onClick={() => copyToClipboard(val)}>
|
||||
{formatValue(val)}
|
||||
</span>
|
||||
{plaintext && <PlainText>{plaintext}</PlainText>}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Card } from 'client/components/Form/Card';
|
||||
import Row from 'client/components/Form/Row';
|
||||
@@ -13,47 +12,51 @@ const LearnMoreInfo = styled.p`
|
||||
}
|
||||
`;
|
||||
|
||||
const formatBytes = (n: number): string => {
|
||||
if (n >= 1048576) return `${(n / 1048576).toFixed(2)} MB`;
|
||||
if (n >= 1024) return `${(n / 1024).toFixed(2)} KB`;
|
||||
return `${Math.round(n)} bytes`;
|
||||
};
|
||||
|
||||
const formatGrams = (g: number): string => {
|
||||
if (g >= 1000) return `${(g / 1000).toFixed(2)} kg`;
|
||||
if (g >= 1) return `${g.toFixed(2)} g`;
|
||||
return `${(g * 1000).toFixed(2)} mg`;
|
||||
};
|
||||
|
||||
const formatKwh = (kwh: number): string => {
|
||||
if (kwh >= 1) return `${kwh.toFixed(3)} kWh`;
|
||||
if (kwh >= 0.001) return `${(kwh * 1000).toFixed(3)} Wh`;
|
||||
return `${(kwh * 1_000_000).toFixed(2)} mWh`;
|
||||
};
|
||||
|
||||
const CarbonCard = (props: { data: any; title: string; actionButtons: any }): JSX.Element => {
|
||||
const carbons = props.data.statistics;
|
||||
const initialUrl = props.data.scanUrl;
|
||||
|
||||
const [carbonData, setCarbonData] = useState<{ c?: number; p?: number }>({});
|
||||
|
||||
useEffect(() => {
|
||||
const fetchCarbonData = async () => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://api.websitecarbon.com/b?url=${encodeURIComponent(initialUrl)}`,
|
||||
);
|
||||
const data = await response.json();
|
||||
setCarbonData(data);
|
||||
} catch (error) {
|
||||
console.error('Error fetching carbon data:', error);
|
||||
}
|
||||
};
|
||||
fetchCarbonData();
|
||||
}, [initialUrl]);
|
||||
const cleanerThan = props.data.cleanerThan;
|
||||
|
||||
return (
|
||||
<Card heading={props.title} actionButtons={props.actionButtons}>
|
||||
{!carbons?.adjustedBytes && !carbonData.c && (
|
||||
<p>Unable to calculate carbon footprint for host</p>
|
||||
)}
|
||||
{!carbons?.adjustedBytes && <p>Unable to calculate carbon footprint for host</p>}
|
||||
{carbons?.adjustedBytes > 0 && (
|
||||
<>
|
||||
<Row lbl="HTML Initial Size" val={`${carbons.adjustedBytes} bytes`} />
|
||||
<Row
|
||||
lbl="CO2 for Initial Load"
|
||||
val={`${(carbons.co2.grid.grams * 1000).toPrecision(4)} grams`}
|
||||
/>
|
||||
<Row lbl="Energy Usage for Load" val={`${(carbons.energy * 1000).toPrecision(4)} KWg`} />
|
||||
<Row lbl="HTML Initial Size" val={formatBytes(carbons.adjustedBytes)} />
|
||||
<Row lbl="CO2 for Initial Load" val={formatGrams(carbons.co2.grid.grams)} />
|
||||
<Row lbl="Energy Usage for Load" val={formatKwh(carbons.energy)} />
|
||||
{cleanerThan > 0 && (
|
||||
<Row lbl="Cleaner than average page (est.)" val={`${cleanerThan}%`} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{carbonData.c && <Row lbl="CO2 Emitted" val={`${carbonData.c} grams`} />}
|
||||
{carbonData.p && <Row lbl="Better than average site by" val={`${carbonData.p}%`} />}
|
||||
<br />
|
||||
<LearnMoreInfo>
|
||||
Learn more at <a href="https://www.websitecarbon.com/">websitecarbon.com</a>
|
||||
Calculated using the{' '}
|
||||
<a
|
||||
href="https://sustainablewebdesign.org/estimating-digital-emissions"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Sustainable Web Model v4
|
||||
</a>
|
||||
</LearnMoreInfo>
|
||||
</Card>
|
||||
);
|
||||
|
||||
@@ -34,20 +34,20 @@ const CookiesCard = (props: { data: any; title: string; actionButtons: any }): J
|
||||
});
|
||||
return (
|
||||
<ExpandableRow
|
||||
key={`cookie-${index}`}
|
||||
key={`header-cookie-${index}-${cookie.name}`}
|
||||
lbl={cookie.name}
|
||||
val={cookie.value}
|
||||
rowList={attributes}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{clientCookies.map((cookie: any) => {
|
||||
{clientCookies.map((cookie: any, index: number) => {
|
||||
const nameValPairs = Object.keys(cookie).map((key: string) => {
|
||||
return { lbl: key, val: cookie[key] };
|
||||
});
|
||||
return (
|
||||
<ExpandableRow
|
||||
key={`cookie-${cookie.name}`}
|
||||
key={`client-cookie-${index}-${cookie.name}`}
|
||||
lbl={cookie.name}
|
||||
val=""
|
||||
rowList={nameValPairs}
|
||||
|
||||
@@ -316,7 +316,10 @@ const AdditionalResources = (props: { url?: string }): JSX.Element => {
|
||||
<br />
|
||||
At the time of listing, all of the above were available and free to use - if this changes,
|
||||
please report it via GitHub (
|
||||
<a href="https://github.com/lissy93/web-check">lissy93/web-check</a>).
|
||||
<a target="_blank" rel="noreferrer" href="https://github.com/lissy93/web-check">
|
||||
lissy93/web-check
|
||||
</a>
|
||||
).
|
||||
</Note>
|
||||
</Card>
|
||||
);
|
||||
|
||||
@@ -44,7 +44,10 @@ const SelfScanMsg = () => {
|
||||
<br />
|
||||
<span>
|
||||
But if you want to see how this site is built, why not check out the{' '}
|
||||
<a href="https://github.com/lissy93/web-check">source code</a>?
|
||||
<a target="_blank" rel="noreferrer" href="https://github.com/lissy93/web-check">
|
||||
source code
|
||||
</a>
|
||||
?
|
||||
</span>
|
||||
<br />
|
||||
<i>Do me a favour, and drop the repo a Star while you're there</i> 😉
|
||||
|
||||
@@ -3,12 +3,6 @@ import { Global, css } from '@emotion/react';
|
||||
const GlobalStyles = () => (
|
||||
<Global
|
||||
styles={css`
|
||||
@font-face {
|
||||
font-family: PTMono;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('/fonts/PTMono.ttf') format('ttf');
|
||||
}
|
||||
body,
|
||||
div,
|
||||
a,
|
||||
|
||||
@@ -223,7 +223,6 @@ const Results = (props: { address?: string }): JSX.Element => {
|
||||
}))}
|
||||
/>
|
||||
<AdditionalResources url={address} />
|
||||
<Footer />
|
||||
<Modal isOpen={modalOpen} closeModal={() => setModalOpen(false)}>
|
||||
{modalContent}
|
||||
</Modal>
|
||||
@@ -234,6 +233,7 @@ const Results = (props: { address?: string }): JSX.Element => {
|
||||
theme="dark"
|
||||
position="bottom-right"
|
||||
/>
|
||||
<Footer />
|
||||
</ResultsOuter>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -12,11 +12,14 @@ interface Props {
|
||||
description?: string;
|
||||
keywords?: string;
|
||||
customSchemaJson?: any;
|
||||
preloadHeadingFont?: boolean;
|
||||
breadcrumbs?: Array<{
|
||||
name: string;
|
||||
item: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
const { preloadHeadingFont = true } = Astro.props;
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
@@ -25,13 +28,17 @@ interface Props {
|
||||
<ClientRouter />
|
||||
<MetaTags {...Astro.props} />
|
||||
<slot name="head" />
|
||||
<link
|
||||
href="/fonts/Hubot-Sans/Hubot-Sans.woff2"
|
||||
as="font"
|
||||
rel="preload"
|
||||
type="font/woff2"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
{
|
||||
preloadHeadingFont && (
|
||||
<link
|
||||
href="/fonts/Hubot-Sans/WOFF2/HubotSans-Regular.woff2"
|
||||
as="font"
|
||||
rel="preload"
|
||||
type="font/woff2"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
)
|
||||
}
|
||||
</head>
|
||||
<body>
|
||||
<slot />
|
||||
|
||||
@@ -14,7 +14,7 @@ if (searchUrl) {
|
||||
}
|
||||
---
|
||||
|
||||
<BaseLayout>
|
||||
<BaseLayout preloadHeadingFont={false}>
|
||||
<Main client:only="react" />
|
||||
</BaseLayout>
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ import Footer from '@components/scafold/Footer.astro';
|
||||
<h3>API Source</h3>
|
||||
<p>View, edit, download or deploy the API from source.</p>
|
||||
<div class="buttons">
|
||||
<a href="https://github.com/xray-web/web-check-api">
|
||||
<a href="https://github.com/xray-web/web-check-api" target="_blank" rel="noreferrer">
|
||||
<img src="/assets/images/github.svg" />
|
||||
GitHub
|
||||
</a>
|
||||
|
||||
17
yarn.lock
17
yarn.lock
@@ -116,14 +116,6 @@
|
||||
send "^1.2.1"
|
||||
server-destroy "^1.0.1"
|
||||
|
||||
"@astrojs/partytown@^2.1.7":
|
||||
version "2.1.7"
|
||||
resolved "https://registry.npmjs.org/@astrojs/partytown/-/partytown-2.1.7.tgz"
|
||||
integrity sha512-dbffmNmJ+sAJ0/aXSaLX4aI04EZS/2C6Mm/+fmd4ikqWO7hV6nIi0sug8Z3c+yqedJNi1swFvpwluWmGjLHNzw==
|
||||
dependencies:
|
||||
"@qwik.dev/partytown" "^0.13.2"
|
||||
mrmime "^2.0.1"
|
||||
|
||||
"@astrojs/prism@4.0.1":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.npmjs.org/@astrojs/prism/-/prism-4.0.1.tgz"
|
||||
@@ -1861,13 +1853,6 @@
|
||||
tar-fs "^3.1.1"
|
||||
yargs "^17.7.2"
|
||||
|
||||
"@qwik.dev/partytown@^0.13.2":
|
||||
version "0.13.2"
|
||||
resolved "https://registry.npmjs.org/@qwik.dev/partytown/-/partytown-0.13.2.tgz"
|
||||
integrity sha512-Umls4bSkuzqLVcGvf8OgwIn/OldproSAbaQ/iYGe8VPYBpl2CaOSxabWwkeC72LDFqxVL0b0q8XlI8MuChDyzg==
|
||||
dependencies:
|
||||
dotenv "^16.4.7"
|
||||
|
||||
"@reduxjs/toolkit@^1.9.0 || 2.x.x":
|
||||
version "2.11.2"
|
||||
resolved "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz"
|
||||
@@ -4251,7 +4236,7 @@ dot-prop@9.0.0, dot-prop@^9.0.0:
|
||||
dependencies:
|
||||
type-fest "^4.18.2"
|
||||
|
||||
dotenv@^16.3.1, dotenv@^16.4.7:
|
||||
dotenv@^16.3.1:
|
||||
version "16.6.1"
|
||||
resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz"
|
||||
integrity sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==
|
||||
|
||||
Reference in New Issue
Block a user