feat: Removed carbon dep, remove partytown, open links in new tab

This commit is contained in:
Alicia Sykes
2026-05-09 16:31:24 +01:00
parent cc72798575
commit 499191becc
14 changed files with 80 additions and 73 deletions

View File

@@ -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,
};
};

View File

@@ -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) {

View File

@@ -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",

View File

@@ -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>}

View File

@@ -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>
);

View File

@@ -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}

View File

@@ -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>
);

View File

@@ -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> 😉

View File

@@ -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,

View File

@@ -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>
);
};

View File

@@ -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 />

View File

@@ -14,7 +14,7 @@ if (searchUrl) {
}
---
<BaseLayout>
<BaseLayout preloadHeadingFont={false}>
<Main client:only="react" />
</BaseLayout>

View File

@@ -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>

View File

@@ -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==