mirror of
https://github.com/nkoehring/250kb-club.git
synced 2025-04-19 13:49:04 +02:00
first working version
This commit is contained in:
parent
8ad2a779cd
commit
bef1d7342b
21 changed files with 1732 additions and 1 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
node_modules
|
44
README.md
44
README.md
|
@ -1,2 +1,44 @@
|
||||||
# 250kb-club
|
# 250kb-club
|
||||||
An exclusive members-only club for web pages weighing no more than 250kb. Inspired by Bredley Taunts 1MB.club
|
|
||||||
|
An exclusive members-only club for web pages weighing no more than 250kb.
|
||||||
|
|
||||||
|
Inspired by [Bredley Taunts 1MB.club](https://1mb.club/).
|
||||||
|
|
||||||
|
## But why?
|
||||||
|
|
||||||
|
I love the idea of a list of webpages that are still reasonably usable with a slow internet connection. But 1MB is, in my honest opinion, still way too much. Nobody wants to wait 10 seconds — on good days — to load a web site. But a very large chunk of the world population isn't gifted with Gigabit internet connections.
|
||||||
|
|
||||||
|
## Adding a web page
|
||||||
|
|
||||||
|
Please add a PR in Github that adds a page to `src/pages.json`. If unsure, you can also write an issue mentioning the website. The website will be added after passing the review.
|
||||||
|
|
||||||
|
## What are those values?
|
||||||
|
|
||||||
|
The values shown in the list are URL, Total Weight, Content Ratio.
|
||||||
|
|
||||||
|
Websites listed here are downloaded and analyzed with
|
||||||
|
(Phantomas)[https://github.com/macbre/phantomas].
|
||||||
|
The total weight is counted and then the size of actual content is measured
|
||||||
|
and shown as a ratio.
|
||||||
|
|
||||||
|
For example: If a website has a total weight of 100kb and 60kb are the
|
||||||
|
documents structure, text, images, videos and so on, then the content ratio
|
||||||
|
is 60%. The rest are extras like CSS, JavaScript and so on. It is hard to
|
||||||
|
say what a good ratio is but my gut feeling is that everything above 20% is
|
||||||
|
pretty good already.
|
||||||
|
|
||||||
|
## Hacking this page
|
||||||
|
|
||||||
|
This page is built with [Svelte](https://svelte.dev). You can clone the repository and run the application in development mode like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://github.com/nkoehring/250kb-club.git
|
||||||
|
cd 250kb-club
|
||||||
|
yarn
|
||||||
|
yarn dev
|
||||||
|
```
|
||||||
|
|
||||||
|
And build the page with `yarn build`.
|
||||||
|
|
||||||
|
The website analysis is done by `compile-list.js` which reads `pages.txt` and
|
||||||
|
writes the results to `src/pages.json`.
|
||||||
|
|
58
compile-list.js
Normal file
58
compile-list.js
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
const fs = require('fs')
|
||||||
|
const phantomas = require('phantomas')
|
||||||
|
const pageData = require('./src/pages.json')
|
||||||
|
|
||||||
|
const INPUT_FILE = './pages.txt'
|
||||||
|
const OUTPUT_FILE = './src/pages.json'
|
||||||
|
const RECHECK_THRESHOLD = 60*60*24*7*1000 // recheck pages older than 1 week
|
||||||
|
|
||||||
|
function calcWeights (url, metrics) {
|
||||||
|
const m = metrics
|
||||||
|
const extraWeight = m.cssSize + m.jsSize + m.webfontSize + m.otherSize
|
||||||
|
const contentWeight = m.htmlSize + m.jsonSize + m.imageSize + m.base64Size + m.videoSize
|
||||||
|
|
||||||
|
return { url, contentWeight, extraWeight, stamp: Date.now() }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generateMetrics (urls) {
|
||||||
|
console.debug('Checking', urls)
|
||||||
|
const metricsList = []
|
||||||
|
const keyedPageData = pageData.reduce((acc, page) => {
|
||||||
|
// stores url/stamp pairs to decide for recheck
|
||||||
|
acc[page.url] = page
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
const knownURLs = Object.keys(keyedPageData)
|
||||||
|
const now = Date.now()
|
||||||
|
|
||||||
|
for (const url of urls) {
|
||||||
|
if (knownURLs.indexOf(url) >= 0) {
|
||||||
|
if (now - keyedPageData[url].stamp < RECHECK_THRESHOLD) {
|
||||||
|
console.debug('skipping known URL', url)
|
||||||
|
metricsList.push(keyedPageData[url]) // push old data to list
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
console.debug('fetching and analyzing', url)
|
||||||
|
const results = await phantomas(url)
|
||||||
|
metricsList.push(calcWeights(url, results.getMetrics()))
|
||||||
|
} catch(error) {
|
||||||
|
console.error(`failed to analyze ${url}`, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(OUTPUT_FILE, JSON.stringify(metricsList))
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`ERROR: failed to write results to ${OUTPUT_FILE}`, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const rawString = fs.readFileSync(INPUT_FILE, 'utf8')
|
||||||
|
const urls = rawString.split('\n').filter(line => line.startsWith('http'))
|
||||||
|
generateMetrics(urls)
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`ERROR: failed to read page list from ${INPUT_FILE}`, err)
|
||||||
|
}
|
23
package.json
Normal file
23
package.json
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"name": "250kb-club",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"scripts": {
|
||||||
|
"build": "rollup -c",
|
||||||
|
"dev": "rollup -c -w",
|
||||||
|
"start": "sirv public"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@rollup/plugin-commonjs": "^14.0.0",
|
||||||
|
"@rollup/plugin-json": "^4.1.0",
|
||||||
|
"@rollup/plugin-node-resolve": "^8.0.0",
|
||||||
|
"phantomas": "^2.0.0",
|
||||||
|
"rollup": "^2.3.4",
|
||||||
|
"rollup-plugin-livereload": "^2.0.0",
|
||||||
|
"rollup-plugin-svelte": "^6.0.0",
|
||||||
|
"rollup-plugin-terser": "^7.0.0",
|
||||||
|
"svelte": "^3.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"sirv-cli": "^1.0.0"
|
||||||
|
}
|
||||||
|
}
|
14
pages.txt
Normal file
14
pages.txt
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
One URL per line.
|
||||||
|
Lines that don't start with http:// or https://, as well as duplicates are ignored.
|
||||||
|
|
||||||
|
https://koehr.in
|
||||||
|
https://koehr.tech
|
||||||
|
https://sjmulder.nl
|
||||||
|
http://cyberia.host
|
||||||
|
https://text.npr.org
|
||||||
|
https://playerone.kevincox.ca
|
||||||
|
https://dotfilehub.com
|
||||||
|
https://manpages.bsd.lv
|
||||||
|
https://danluu.com
|
||||||
|
https://gtf.io
|
||||||
|
http://minid.net
|
2
public/build/bundle.css
Normal file
2
public/build/bundle.css
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
/*# sourceMappingURL=bundle.css.map */
|
8
public/build/bundle.css.map
Normal file
8
public/build/bundle.css.map
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"version": 3,
|
||||||
|
"file": "bundle.css",
|
||||||
|
"sources": [],
|
||||||
|
"sourcesContent": [],
|
||||||
|
"names": [],
|
||||||
|
"mappings": ""
|
||||||
|
}
|
2
public/build/bundle.js
Normal file
2
public/build/bundle.js
Normal file
File diff suppressed because one or more lines are too long
1
public/build/bundle.js.map
Normal file
1
public/build/bundle.js.map
Normal file
File diff suppressed because one or more lines are too long
BIN
public/favicon.png
Normal file
BIN
public/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 529 B |
100
public/global.css
Normal file
100
public/global.css
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
body {
|
||||||
|
font: 16px/1.4 sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: white;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2.2em;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
body>header,main,body>footer {
|
||||||
|
max-width: calc(720px - 2em);
|
||||||
|
width: calc(100% - 2em);
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
margin: 3em auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
a,a:visited {
|
||||||
|
color: currentColor;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
select, button {
|
||||||
|
margin: 0 .5em;
|
||||||
|
padding: .25em .5em;
|
||||||
|
border: 2px solid gray;
|
||||||
|
background: none;
|
||||||
|
color: currentColor;
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
border-top: 1px solid lightgrey;
|
||||||
|
margin: 3rem auto 0;
|
||||||
|
font-size: 85%;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
background-color: #0002;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: .5em .5em 0;
|
||||||
|
height: 2em;
|
||||||
|
line-height: 2em;
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry > .url {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
width: 60%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.entry > .size, .entry > .ratio {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: 20%;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry-size-bar, .entry-ratio-bar {
|
||||||
|
height: 0;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
border-bottom: 2px solid;
|
||||||
|
}
|
||||||
|
.entry-size-bar.highlighted, .entry-ratio-bar.highlighted {
|
||||||
|
border-bottom-width: 4px;
|
||||||
|
}
|
||||||
|
.entry-size-bar {
|
||||||
|
border-bottom-color: #966;
|
||||||
|
width: calc(var(--size)/250 * 100%);
|
||||||
|
}
|
||||||
|
.entry-ratio-bar {
|
||||||
|
border-bottom-color: #669;
|
||||||
|
width: var(--ratio);
|
||||||
|
}
|
||||||
|
|
||||||
|
.float-right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body { background: #222; color: white; }
|
||||||
|
}
|
37
public/index.html
Normal file
37
public/index.html
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>The 250kb Club</title>
|
||||||
|
<meta name="description" content="An exclusive membership for web pages presenting themselves in no more than 250kb.">
|
||||||
|
<link rel="icon" href="/favicon.png" type="image/x-icon">
|
||||||
|
<link rel='stylesheet' href='/global.css'>
|
||||||
|
<script defer src='/build/bundle.js'></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<h1>The 250kb Club</h1>
|
||||||
|
<p>
|
||||||
|
The WWW has become a bloated mess. Many pages are loading megabytes of Javascript to show you a few kilobytes of content.
|
||||||
|
These things are a <strong>cancerous growth</strong> on the web that we should stand up against.
|
||||||
|
</p>
|
||||||
|
<p>We can make a difference - no matter how small it may seem. The <em>250kb Club</em> is a collection of web pages that focus on performance, efficiency and accessibility.</p>
|
||||||
|
<p>
|
||||||
|
If you'd like to suggest a web page to add to this collection,
|
||||||
|
<a href="https://github.com/nkoehring/250kb-club" rel="noopener" target="_blank">open a pull request or a ticket in the official Github repository</a>.
|
||||||
|
The site will be reviewed and, if applicable, added to the list below.
|
||||||
|
</p>
|
||||||
|
<p>If your pages exceeds 250kb, you might consider <a href="https://1MB.club" rel="noopener" target="_blank">1MB.club</a> which is the inspiration for this page.</p>
|
||||||
|
</header>
|
||||||
|
<main id="members-table">
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
<p>
|
||||||
|
Made with ♥ for a performant web by <a href="https://koehr.in" rel="noopener" target="_blank">Norman Köhring</a>.
|
||||||
|
Inspired by <a href="https://uglyduck.ca" rel="noopener" target="_blank">Bradley Taunt</a>s <a href="https://1MB.club" rel="noopener" target="_blank">1MB.club</a>
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
<script data-goatcounter="https://250kb-club.goatcounter.com/count" async src="//gc.zgo.at/count.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
83
rollup.config.js
Normal file
83
rollup.config.js
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
import svelte from 'rollup-plugin-svelte';
|
||||||
|
import resolve from '@rollup/plugin-node-resolve';
|
||||||
|
import commonjs from '@rollup/plugin-commonjs';
|
||||||
|
import json from '@rollup/plugin-json';
|
||||||
|
import livereload from 'rollup-plugin-livereload';
|
||||||
|
import { terser } from 'rollup-plugin-terser';
|
||||||
|
|
||||||
|
const isProduction = !process.env.ROLLUP_WATCH;
|
||||||
|
|
||||||
|
function serve() {
|
||||||
|
let server;
|
||||||
|
|
||||||
|
function toExit() {
|
||||||
|
if (server) server.kill(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
writeBundle() {
|
||||||
|
if (server) return;
|
||||||
|
server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
|
||||||
|
stdio: ['ignore', 'inherit', 'inherit'],
|
||||||
|
shell: true
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on('SIGTERM', toExit);
|
||||||
|
process.on('exit', toExit);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
input: 'src/main.js',
|
||||||
|
output: {
|
||||||
|
sourcemap: true,
|
||||||
|
format: 'iife',
|
||||||
|
name: 'app',
|
||||||
|
file: 'public/build/bundle.js'
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
svelte({
|
||||||
|
// enable run-time checks when not in production
|
||||||
|
dev: !isProduction,
|
||||||
|
// we'll extract any component CSS out into
|
||||||
|
// a separate file - better for performance
|
||||||
|
css: css => {
|
||||||
|
css.write('bundle.css');
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
// If you have external dependencies installed from
|
||||||
|
// npm, you'll most likely need these plugins. In
|
||||||
|
// some cases you'll need additional configuration -
|
||||||
|
// consult the documentation for details:
|
||||||
|
// https://github.com/rollup/plugins/tree/master/packages/commonjs
|
||||||
|
resolve({
|
||||||
|
browser: true,
|
||||||
|
dedupe: ['svelte']
|
||||||
|
}),
|
||||||
|
commonjs(),
|
||||||
|
|
||||||
|
json({
|
||||||
|
exclude: ['node_modules/**'],
|
||||||
|
preferConst: true,
|
||||||
|
compact: true,
|
||||||
|
namedExports: false
|
||||||
|
}),
|
||||||
|
|
||||||
|
// In dev mode, call `npm run start` once
|
||||||
|
// the bundle has been generated
|
||||||
|
!isProduction && serve(),
|
||||||
|
|
||||||
|
// Watch the `public` directory and refresh the
|
||||||
|
// browser on changes when not in production
|
||||||
|
!isProduction && livereload('public'),
|
||||||
|
|
||||||
|
// If we're building for production (npm run build
|
||||||
|
// instead of npm run dev), minify
|
||||||
|
isProduction && terser()
|
||||||
|
],
|
||||||
|
watch: {
|
||||||
|
clearScreen: false
|
||||||
|
}
|
||||||
|
};
|
128
scripts/setupTypeScript.js
Normal file
128
scripts/setupTypeScript.js
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
/** This script modifies the project to support TS code in .svelte files like:
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export let name: string;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
As well as validating the code for CI.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** To work on this script:
|
||||||
|
rm -rf test-template template && git clone sveltejs/template test-template && node scripts/setupTypeScript.js test-template
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require("fs")
|
||||||
|
const path = require("path")
|
||||||
|
const { argv } = require("process")
|
||||||
|
|
||||||
|
const projectRoot = argv[2] || path.join(__dirname, "..")
|
||||||
|
|
||||||
|
// Add deps to pkg.json
|
||||||
|
const packageJSON = JSON.parse(fs.readFileSync(path.join(projectRoot, "package.json"), "utf8"))
|
||||||
|
packageJSON.devDependencies = Object.assign(packageJSON.devDependencies, {
|
||||||
|
"svelte-check": "^1.0.0",
|
||||||
|
"svelte-preprocess": "^4.0.0",
|
||||||
|
"@rollup/plugin-typescript": "^6.0.0",
|
||||||
|
"typescript": "^3.9.3",
|
||||||
|
"tslib": "^2.0.0",
|
||||||
|
"@tsconfig/svelte": "^1.0.0"
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add script for checking
|
||||||
|
packageJSON.scripts = Object.assign(packageJSON.scripts, {
|
||||||
|
"validate": "svelte-check"
|
||||||
|
})
|
||||||
|
|
||||||
|
// Write the package JSON
|
||||||
|
fs.writeFileSync(path.join(projectRoot, "package.json"), JSON.stringify(packageJSON, null, " "))
|
||||||
|
|
||||||
|
// mv src/main.js to main.ts - note, we need to edit rollup.config.js for this too
|
||||||
|
const beforeMainJSPath = path.join(projectRoot, "src", "main.js")
|
||||||
|
const afterMainTSPath = path.join(projectRoot, "src", "main.ts")
|
||||||
|
fs.renameSync(beforeMainJSPath, afterMainTSPath)
|
||||||
|
|
||||||
|
// Switch the app.svelte file to use TS
|
||||||
|
const appSveltePath = path.join(projectRoot, "src", "App.svelte")
|
||||||
|
let appFile = fs.readFileSync(appSveltePath, "utf8")
|
||||||
|
appFile = appFile.replace("<script>", '<script lang="ts">')
|
||||||
|
appFile = appFile.replace("export let name;", 'export let name: string;')
|
||||||
|
fs.writeFileSync(appSveltePath, appFile)
|
||||||
|
|
||||||
|
// Edit rollup config
|
||||||
|
const rollupConfigPath = path.join(projectRoot, "rollup.config.js")
|
||||||
|
let rollupConfig = fs.readFileSync(rollupConfigPath, "utf8")
|
||||||
|
|
||||||
|
// Edit imports
|
||||||
|
rollupConfig = rollupConfig.replace(`'rollup-plugin-terser';`, `'rollup-plugin-terser';
|
||||||
|
import sveltePreprocess from 'svelte-preprocess';
|
||||||
|
import typescript from '@rollup/plugin-typescript';`)
|
||||||
|
|
||||||
|
// Replace name of entry point
|
||||||
|
rollupConfig = rollupConfig.replace(`'src/main.js'`, `'src/main.ts'`)
|
||||||
|
|
||||||
|
// Add preprocess to the svelte config, this is tricky because there's no easy signifier.
|
||||||
|
// Instead we look for `css:` then the next `}` and add the preprocessor to that
|
||||||
|
let foundCSS = false
|
||||||
|
let match
|
||||||
|
|
||||||
|
// https://regex101.com/r/OtNjwo/1
|
||||||
|
const configEditor = new RegExp(/css:.|\n*}/gmi)
|
||||||
|
while (( match = configEditor.exec(rollupConfig)) != null) {
|
||||||
|
if (foundCSS) {
|
||||||
|
const endOfCSSIndex = match.index + 1
|
||||||
|
rollupConfig = rollupConfig.slice(0, endOfCSSIndex) + ",\n preprocess: sveltePreprocess()," + rollupConfig.slice(endOfCSSIndex);
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (match[0].includes("css:")) foundCSS = true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Add TypeScript
|
||||||
|
rollupConfig = rollupConfig.replace(
|
||||||
|
'commonjs(),',
|
||||||
|
'commonjs(),\n\t\ttypescript({\n\t\t\tsourceMap: !production,\n\t\t\tinlineSources: !production\n\t\t}),'
|
||||||
|
);
|
||||||
|
fs.writeFileSync(rollupConfigPath, rollupConfig)
|
||||||
|
|
||||||
|
// Add TSConfig
|
||||||
|
const tsconfig = `{
|
||||||
|
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||||
|
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["node_modules/*", "__sapper__/*", "public/*"]
|
||||||
|
}`
|
||||||
|
const tsconfigPath = path.join(projectRoot, "tsconfig.json")
|
||||||
|
fs.writeFileSync(tsconfigPath, tsconfig)
|
||||||
|
|
||||||
|
// Delete this script, but not during testing
|
||||||
|
if (!argv[2]) {
|
||||||
|
// Remove the script
|
||||||
|
fs.unlinkSync(path.join(__filename))
|
||||||
|
|
||||||
|
// Check for Mac's DS_store file, and if it's the only one left remove it
|
||||||
|
const remainingFiles = fs.readdirSync(path.join(__dirname))
|
||||||
|
if (remainingFiles.length === 1 && remainingFiles[0] === '.DS_store') {
|
||||||
|
fs.unlinkSync(path.join(__dirname, '.DS_store'))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the scripts folder is empty
|
||||||
|
if (fs.readdirSync(path.join(__dirname)).length === 0) {
|
||||||
|
// Remove the scripts folder
|
||||||
|
fs.rmdirSync(path.join(__dirname))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds the extension recommendation
|
||||||
|
fs.mkdirSync(path.join(projectRoot, ".vscode"))
|
||||||
|
fs.writeFileSync(path.join(projectRoot, ".vscode", "extensions.json"), `{
|
||||||
|
"recommendations": ["svelte.svelte-vscode"]
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
console.log("Converted to TypeScript.")
|
||||||
|
|
||||||
|
if (fs.existsSync(path.join(projectRoot, "node_modules"))) {
|
||||||
|
console.log("\nYou will need to re-run your dependency manager to get started.")
|
||||||
|
}
|
71
src/App.svelte
Normal file
71
src/App.svelte
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
<script>
|
||||||
|
import InfoPopup from './InfoPopup.svelte'
|
||||||
|
import Link from './Link.svelte'
|
||||||
|
import data from './pages.json'
|
||||||
|
|
||||||
|
const yellowSizeThreshhold = 200
|
||||||
|
const redSizeThreshhold = 225
|
||||||
|
|
||||||
|
const yellowRatioThreshhold = 50
|
||||||
|
const redRatioThreshhold = 25
|
||||||
|
|
||||||
|
const pages = data.map(page => {
|
||||||
|
const totalWeigth = page.contentWeight + page.extraWeight
|
||||||
|
const size = Math.round(totalWeigth / 1024)
|
||||||
|
const ratio = Math.round(page.contentWeight * 100 / totalWeigth)
|
||||||
|
|
||||||
|
return { url: page.url, size, ratio }
|
||||||
|
})
|
||||||
|
|
||||||
|
const sortParameters = ['size', 'ratio']
|
||||||
|
let sortParam = sortParameters[0]
|
||||||
|
let showInfoPopup = false
|
||||||
|
|
||||||
|
$: sortedPages = pages.sort((a, b) => {
|
||||||
|
return sortParam === 'size' ? a.size - b.size : b.ratio - a.ratio
|
||||||
|
})
|
||||||
|
|
||||||
|
function stripped (url) {
|
||||||
|
return url.replaceAll(/(^https?:\/\/|\/$)/g, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleInfo () { showInfoPopup = !showInfoPopup }
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<header>
|
||||||
|
Sort by:
|
||||||
|
<select bind:value={sortParam}>
|
||||||
|
{#each sortParameters as param}
|
||||||
|
<option value={param}>content-{param}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
<button class="float-right" on:click={toggleInfo}>{showInfoPopup ? 'x' : 'How does this work?'}</button>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{#if showInfoPopup}
|
||||||
|
<InfoPopup />
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
{#each sortedPages as page}
|
||||||
|
<li style={`--size:${page.size};--ratio:${page.ratio}%`}>
|
||||||
|
<div class="entry">
|
||||||
|
<span class="url"><Link href={page.url}>{stripped(page.url)}</Link></span>
|
||||||
|
<span class="size">{page.size}kb</span>
|
||||||
|
<span class="ratio">{page.ratio}%</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="entry-size-bar"
|
||||||
|
class:highlighted={sortParam === 'size'}
|
||||||
|
class:yellow={page.size > yellowSizeThreshhold}
|
||||||
|
class:red={page.size > redSizeThreshhold}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="entry-ratio-bar"
|
||||||
|
class:highlighted={sortParam === 'ratio'}
|
||||||
|
class:yellow={page.ratio > yellowRatioThreshhold}
|
||||||
|
class:red={page.ratio > redRatioThreshhold}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ol>
|
30
src/InfoPopup.svelte
Normal file
30
src/InfoPopup.svelte
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<script>
|
||||||
|
import Link from './Link.svelte'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<article id="info-popup">
|
||||||
|
<header>
|
||||||
|
<h1>Technical Details</h1>
|
||||||
|
</header>
|
||||||
|
<p>
|
||||||
|
The values shown in the list are URL, Total Weight, Content Ratio.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Websites listed here are downloaded and analyzed with
|
||||||
|
<Link href="https://github.com/macbre/phantomas">Phantomas</Link>.
|
||||||
|
The total weight is counted and then the size of actual content is measured
|
||||||
|
and shown as a ratio.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
For example: If a website has a total weight of 100kb and 60kb are the
|
||||||
|
documents structure, text, images, videos and so on, then the content ratio
|
||||||
|
is 60%. The rest are extras like CSS, JavaScript and so on. It is hard to
|
||||||
|
say what a good ratio is but my gut feeling is that everything above 20% is
|
||||||
|
pretty good already.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Disclaimer:</strong> Currently, inline scripts and styles are
|
||||||
|
measured as content due to technical limitations of Phantomas. This will
|
||||||
|
hopefully be fixed soon.
|
||||||
|
</p>
|
||||||
|
</article>
|
5
src/Link.svelte
Normal file
5
src/Link.svelte
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<script>
|
||||||
|
export let href;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<a {href} rel="noopener" target="_blank"><slot /></a>
|
5
src/main.js
Normal file
5
src/main.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import App from './App.svelte';
|
||||||
|
|
||||||
|
var app = new App({ target: document.getElementById('members-table') });
|
||||||
|
|
||||||
|
export default app;
|
1
src/pages.json
Normal file
1
src/pages.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
[{"url":"https://koehr.in","contentWeight":23078,"extraWeight":66537,"stamp":1606004545427},{"url":"https://koehr.tech","contentWeight":4964,"extraWeight":20108,"stamp":1606004547391},{"url":"https://sjmulder.nl","contentWeight":2361,"extraWeight":0,"stamp":1606004663706},{"url":"http://cyberia.host","contentWeight":1191,"extraWeight":0,"stamp":1606004664417},{"url":"https://text.npr.org","contentWeight":2760,"extraWeight":0,"stamp":1606004665037},{"url":"https://playerone.kevincox.ca","contentWeight":1904,"extraWeight":42661,"stamp":1606004665881},{"url":"https://dotfilehub.com","contentWeight":961,"extraWeight":1281,"stamp":1606004667422},{"url":"https://manpages.bsd.lv","contentWeight":7045,"extraWeight":1346,"stamp":1606004669823},{"url":"https://danluu.com","contentWeight":2895,"extraWeight":0,"stamp":1606004670441},{"url":"https://gtf.io","contentWeight":2040,"extraWeight":2752,"stamp":1606004671103},{"url":"http://minid.net","contentWeight":4110,"extraWeight":0,"stamp":1606004672171}]
|
14
src/pages.json.bak
Normal file
14
src/pages.json.bak
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"url": "https://koehr.in",
|
||||||
|
"contentWeight": 23078,
|
||||||
|
"extraWeight": 66538,
|
||||||
|
"stamp": 1606002516753
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://koehr.tech",
|
||||||
|
"contentWeight": 4964,
|
||||||
|
"extraWeight": 20108,
|
||||||
|
"stamp": 1606002519511
|
||||||
|
}
|
||||||
|
]
|
Loading…
Add table
Reference in a new issue