Skip to content

Commit 1cc10df

Browse files
committed
Make webpack config less hacky
1 parent 8089d74 commit 1cc10df

8 files changed

+102
-93
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Mandelbrot.site is built using modern web technologies to deliver a high-perform
4343

4444
For performance optimization, it employs [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) via the [threads.js](https://github.com/andywer/threads.js) library. This setup prevents intensive computations from blocking the main browser thread by creating a pool of workers that handle the generation of Mandelbrot set tiles in parallel. A key optimization technique used is "rectangle checking," which saves computation time for areas entirely within the set by checking only the perimeter of a tile.
4545

46-
The application leverages [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) to prioritize a local-first experience, ensuring that users can explore the Mandelbrot set and or with minimal network dependencies.
46+
It is a [Progressive Web App](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps) (PWA), leveraging [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) to prioritize a local-first experience. This ensures that users can explore the Mandelbrot set with minimal network dependencies.
4747

4848
This robust architecture ensures that Mandelbrot.site provides a seamless and responsive experience for users exploring the intricate details of the Mandelbrot set through an online interface.
4949

client/css/styles.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ body {
5454
sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
5555
}
5656

57-
html {
57+
html.fractal-root {
5858
height: 100%;
5959
width: 100%;
6060

client/html/blog-template.html

+23-17
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,38 @@
33
<head>
44
<meta charset="utf-8" />
55
<!-- HTML Meta Tags -->
6-
<title>{{title}} | Mandelbrot Set Explorer</title>
7-
<meta name="description" content="{{description}}" />
8-
<link rel="canonical" href="https://mandelbrot.site/{{slug}}" />
6+
<title><%= title %> | Mandelbrot Set Explorer</title>
7+
<meta name="description" content="<%= description %>" />
8+
<link rel="canonical" href="https://mandelbrot.site/<%= slug %>" />
99

1010
<!-- Google / Search Engine Tags -->
11-
<meta itemprop="name" content="{{title}} | Mandelbrot Set Explorer" />
12-
<meta itemprop="description" content="{{description}}" />
11+
<meta itemprop="name" content="<%= title %> | Mandelbrot Set Explorer" />
12+
<meta itemprop="description" content="<%= description %>" />
1313
<meta
1414
itemprop="image"
1515
content="https://mandelbrot.site/static/site-image.png"
1616
/>
1717

1818
<!-- Facebook Meta Tags -->
19-
<meta property="og:url" content="https://mandelbrot.site/{{slug}}" />
19+
<meta property="og:url" content="https://mandelbrot.site/<%= slug %>" />
2020
<meta property="og:type" content="website" />
21-
<meta property="og:title" content="{{title}} | Mandelbrot Set Explorer" />
22-
<meta property="og:description" content="{{description}}" />
21+
<meta
22+
property="og:title"
23+
content="<%= title %> | Mandelbrot Set Explorer"
24+
/>
25+
<meta property="og:description" content="<%= description %>" />
2326
<meta
2427
property="og:image"
2528
content="https://mandelbrot.site/static/site-image.png"
2629
/>
2730

2831
<!-- Twitter Meta Tags -->
2932
<meta name="twitter:card" content="summary_large_image" />
30-
<meta name="twitter:title" content="{{title}} | Mandelbrot Set Explorer" />
31-
<meta name="twitter:description" content="{{description}}" />
33+
<meta
34+
name="twitter:title"
35+
content="<%= title %> | Mandelbrot Set Explorer"
36+
/>
37+
<meta name="twitter:description" content="<%= description %>" />
3238
<meta
3339
name="twitter:image"
3440
content="https://mandelbrot.site/static/site-image.png"
@@ -72,39 +78,39 @@ <h2 class="site-name">Mandelbrot.site</h2>
7278
<li>
7379
<a
7480
href="/how-mandelbrot-site-was-built"
75-
class="{{howMandelbrotSiteWasBuiltClass}}"
81+
class="<%= howMandelbrotSiteWasBuiltClass %>"
7682
>
7783
How Mandelbrot.site Was Built
7884
</a>
7985
</li>
8086
<li>
8187
<a
8288
href="/what-is-mandelbrot-set"
83-
class="{{whatIsMandelbrotSetClass}}"
89+
class="<%= whatIsMandelbrotSetClass %>"
8490
>
8591
What Is the Mandelbrot Set?
8692
</a>
8793
</li>
8894
<li>
8995
<a
9096
href="/history-of-mandelbrot-set"
91-
class="{{historyOfMandelbrotSetClass}}"
97+
class="<%= historyOfMandelbrotSetClass %>"
9298
>
9399
History of the Mandelbrot Set
94100
</a>
95101
</li>
96102
<li>
97103
<a
98104
href="/who-was-benoit-mandelbrot"
99-
class="{{whoWasBenoitMandelbrotClass}}"
105+
class="<%= whoWasBenoitMandelbrotClass %>"
100106
>
101107
Who Was Benoit Mandelbrot?
102108
</a>
103109
</li>
104110
<li>
105111
<a
106112
href="/why-mandelbrot-set-important"
107-
class="{{whyMandelbrotSetImportantClass}}"
113+
class="<%= whyMandelbrotSetImportantClass %>"
108114
>
109115
Why the Mandelbrot Set Is Important
110116
</a>
@@ -128,8 +134,8 @@ <h2 class="site-name">Mandelbrot.site</h2>
128134
</nav>
129135
<main>
130136
<div class="blog-content">
131-
<h1>{{title}}</h1>
132-
{{content}}
137+
<h1><%= title %></h1>
138+
<%= content %>
133139
<div class="bottom-spacer"></div>
134140
</div>
135141
</main>

client/html/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!doctype html>
2-
<html lang="en">
2+
<html lang="en" class="fractal-root">
33
<head>
44
<meta charset="utf-8" />
55
<!-- HTML Meta Tags -->

client/js/index.ts

+23-26
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,34 @@
11
import "./static";
22
import MandelbrotMap from "./MandelbrotMap";
33

4-
if ("serviceWorker" in navigator) {
5-
window.addEventListener("load", () => {
4+
window.addEventListener("load", () => {
5+
if ("serviceWorker" in navigator) {
66
navigator.serviceWorker
77
.register("/service-worker.js")
8-
.then(() => {
9-
console.log("Service worker registered.");
10-
})
118
.catch((err: unknown) => {
129
console.error("Service worker registration failed:", err);
1310
});
14-
});
15-
}
11+
}
1612

17-
window.addEventListener("load", () => {
18-
new MandelbrotMap({
19-
htmlId: "leaflet",
20-
initialConfig: {
21-
iterations: 200,
22-
exponent: 2,
23-
colorScheme: "turbo",
24-
lightenAmount: 0,
25-
saturateAmount: 0,
26-
shiftHueAmount: 0,
27-
colorSpace: 2,
28-
reverseColors: false,
29-
highDpiTiles: false,
30-
smoothColoring: true,
13+
if (document.getElementById("leaflet")) {
14+
new MandelbrotMap({
15+
htmlId: "leaflet",
16+
initialConfig: {
17+
iterations: 200,
18+
exponent: 2,
19+
colorScheme: "turbo",
20+
lightenAmount: 0,
21+
saturateAmount: 0,
22+
shiftHueAmount: 0,
23+
colorSpace: 2,
24+
reverseColors: false,
25+
highDpiTiles: false,
26+
smoothColoring: true,
3127

32-
re: -0.5,
33-
im: 0,
34-
zoom: 3,
35-
},
36-
});
28+
re: -0.5,
29+
im: 0,
30+
zoom: 3,
31+
},
32+
});
33+
}
3734
});

client/package-lock.json

+14-14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"@typescript-eslint/eslint-plugin": "^6.20.0",
2626
"@typescript-eslint/parser": "^6.20.0",
2727
"@wasm-tool/wasm-pack-plugin": "^1.7.0",
28-
"css-loader": "^6.9.1",
28+
"css-loader": "^7.1.2",
2929
"dotenv-webpack": "^8.1.0",
3030
"eslint": "^8.56.0",
3131
"file-loader": "^6.2.0",
@@ -34,8 +34,8 @@
3434
"marked": "^12.0.2",
3535
"mini-css-extract-plugin": "^2.7.7",
3636
"prettier": "3.3.3",
37-
"sass": "^1.70.0",
38-
"sass-loader": "^14.1.0",
37+
"sass": "^1.83.4",
38+
"sass-loader": "^16.0.4",
3939
"ts-loader": "^9.5.1",
4040
"typescript": "^5.3.3",
4141
"webpack": "^5.90.1",

client/webpack.config.js

+36-30
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,28 @@
11
const path = require("path");
22
const HtmlWebpackPlugin = require("html-webpack-plugin");
33
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
4-
const dist = path.resolve(__dirname, "dist");
54
const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");
65
const { marked } = require("marked");
76
const frontMatter = require("front-matter");
87
const fs = require("fs");
9-
const template = require("lodash/template");
108
const camelCase = require("lodash/camelCase");
119
const fromPairs = require("lodash/fromPairs");
1210
const Dotenv = require("dotenv-webpack");
1311
const WorkboxPlugin = require("workbox-webpack-plugin");
1412

15-
if (!fs.existsSync("./dist")) {
16-
fs.mkdirSync("./dist");
17-
}
13+
const dist = path.resolve(__dirname, "dist");
14+
15+
function getBlogPostPlugins() {
16+
const blogDir = "./blog";
17+
const blogPostPlugins = [];
1818

19-
const privacyPolicySrc = path.join(__dirname, "html", "privacy-policy.html");
20-
const privacyPolicyDest = path.join(__dirname, "dist", "privacy-policy.html");
21-
fs.copyFileSync(privacyPolicySrc, privacyPolicyDest);
19+
for (const file of fs.readdirSync(blogDir)) {
20+
if (!file.endsWith(".md")) continue;
2221

23-
const blogDir = "./blog";
24-
for (const file of fs.readdirSync(blogDir)) {
25-
if (file.endsWith(".md")) {
2622
const md = fs.readFileSync(path.join(blogDir, file), "utf8");
27-
const metadata = frontMatter(md).attributes;
28-
const html = marked(md.replace(/^---$.*^---$/ms, ""));
23+
const { attributes, body } = frontMatter(md);
24+
const html = marked(body);
2925
const htmlFile = file.replace(".md", ".html");
30-
const blogTemplate = fs.readFileSync("./html/blog-template.html", "utf8");
31-
3226
const slug = htmlFile.replace(/\.html$/, "");
3327
const slugCamel = camelCase(slug);
3428

@@ -40,24 +34,36 @@ for (const file of fs.readdirSync(blogDir)) {
4034
"whoWasBenoitMandelbrotClass",
4135
"whyMandelbrotSetImportantClass",
4236
].map((c) => {
43-
return [c, slugCamel === c.split("Class")[0] ? "active" : ""];
37+
const checkSlug = c.split("Class")[0];
38+
return [c, slugCamel === checkSlug ? "active" : ""];
4439
}),
4540
);
4641

47-
const result = template(blogTemplate, {
48-
interpolate: /{{([\s\S]+?)}}/g,
49-
})({
50-
title: metadata.title,
51-
description: metadata.excerpt,
52-
content: html,
53-
slug,
54-
...linkClasses,
55-
});
56-
57-
fs.writeFileSync(path.join("./dist", htmlFile), result);
42+
blogPostPlugins.push(
43+
new HtmlWebpackPlugin({
44+
filename: htmlFile,
45+
template: "./html/blog-template.html",
46+
templateParameters: {
47+
title: attributes.title,
48+
description: attributes.excerpt,
49+
content: html,
50+
slug,
51+
...linkClasses,
52+
},
53+
}),
54+
);
5855
}
56+
57+
return blogPostPlugins;
5958
}
6059

60+
const privacyPolicyPlugin = new HtmlWebpackPlugin({
61+
filename: "privacy-policy.html",
62+
template: path.join(__dirname, "html", "privacy-policy.html"),
63+
});
64+
65+
const blogPostPlugins = getBlogPostPlugins();
66+
6167
const oneWeekInSeconds = 60 * 60 * 24 * 7;
6268

6369
const workbox = new WorkboxPlugin.GenerateSW({
@@ -81,13 +87,13 @@ const workbox = new WorkboxPlugin.GenerateSW({
8187
const appConfig = {
8288
entry: "./js/index.ts",
8389
plugins: [
84-
new Dotenv({
85-
systemvars: true,
86-
}),
90+
new Dotenv({ systemvars: true }),
8791
new HtmlWebpackPlugin({
8892
template: "html/index.html",
8993
root: path.resolve(__dirname, "."),
9094
}),
95+
...blogPostPlugins,
96+
privacyPolicyPlugin,
9197
new MiniCssExtractPlugin(),
9298
workbox,
9399
],

0 commit comments

Comments
 (0)