redo a lot of things
This commit is contained in:
parent
7d6d0c79e1
commit
a911ee93f8
@ -1 +1,40 @@
|
||||
I promise I'm not as bad at writing code as I am at writing about pages
|
||||
> Software engineer and SRE lead with 7+ years of experience designing, developing, and deploying high-quality, stable, and scalable software solutions.
|
||||
|
||||
### CV/Resume
|
||||
|
||||
If you're looking for my resume, you won't find it here. If you're interested in hiring me, then you can email me for a copy, or find it on my LinkedIn or Wellfound profile.
|
||||
|
||||
What you're going to find here, is going to be a lot more casual, incomprehensible, and just overall baffling. You can save yourself the confusion by closing the tab now. If this is your prefered means of consuming information, feel free to check out [skills](#skills) and [my work](/#my-work).
|
||||
|
||||
### _literally who_
|
||||
|
||||
I'm a person[[citation needed](https://en.wikipedia.org/wiki/Wikipedia:Citation_needed)] that does things on computers. Sometimes I fix them, break them, make them do things you want them to do, or make them do things I want them to do.
|
||||
|
||||
If you want to hire me, I am proud to speak the tongue of the demons we trapped inside the crushed up grains of sand you're using to browse this website.
|
||||
|
||||
#### Skills
|
||||
|
||||
My first skill would be always finding the time to interject that I don't really believe in tooling experience as a gauge of capability at all, but I've decided to list it separately. The fact that it's ambiguous what does and doesn't belong under "skills" is proof of this issue, but my personal criterea is "used a significant amount, in a significant way, had no issues understanding or using"
|
||||
|
||||
**Note: list is limited by what happened to come to mind at the time**
|
||||
|
||||
Personal favourites bolded, though I'm not much of an evangelist or ideologue, I just like to get things done.
|
||||
|
||||
- **Rust**, Python, **OCaml**, Nim, Go, C++, JavaScript/TypeScript, PureScript, **Elm**
|
||||
- **NixOS**, CentOS, Debian, **deploy-rs**, NixOps, **Terraform**, Alpine, **Nomad**, Buildroot
|
||||
- Docker, **Podman**, Firecracker, LXD, **Linux namespaces**, **cgroups**, QEMU, **KVM**, Hyper-V
|
||||
- **Wireguard**, **Tailscale**, OpenVPN, **Prometheus**, AlertManager, **Grafana**, Kibana, **Vault**, **Consul**
|
||||
- **PostgreSQL**, MySQL, **SQLite**, MongoDB, CouchDB, RethinkDB, **Redis**, Memcached, RabbitMQ, AWS SQS
|
||||
- HTML, CSS, RiotJS, EdenJS, Svelte, Vue, React/Redux, **elm-ui**, **GraphQL**
|
||||
|
||||
### wait are you the guy wh-
|
||||
|
||||
You could be about to ask about 100s of things. I have no idea which one, but the answer is probably yes.
|
||||
|
||||
### do you really really get emails here?
|
||||
|
||||
Yes. I do. If you reached this page, and still don't know the answer to this, then chances are I have been arguing with you about it for a very long time now. You seen I have a website, you have no reason not to believe me at this point.
|
||||
|
||||
#### but your email starts wit-
|
||||
|
||||
I know. It works. Trust me.
|
||||
|
@ -1,17 +1,18 @@
|
||||
# notgne2's pages
|
||||
**_Yes, it's a real website, and no I didn't make up a fake email address_**
|
||||
|
||||
## 🍆 ...
|
||||
## 🤔...
|
||||
|
||||
Passionate shitposter and software engineer.
|
||||
I make things for computers to run, and make computers run things.
|
||||
|
||||
Sometimes I have posts [here](/posts/) (last updated: Mar 25 2022) but I don't make many.
|
||||
Sometimes I write posts [here](/posts/) (last updated: Mar 25 2022) but I don't make many.
|
||||
|
||||
## My work
|
||||
|
||||
I have worked significant amounts on various projects, most recently and/or notably being:
|
||||
|
||||
- [deploy-rs](https://github.com/serokell/deploy-rs)
|
||||
- [gm8.app](https://gm8.app) - Service for purchasing and managing servers over Discord
|
||||
- [flox](https://github.com/flox/flox) - User friendly Nix-based environment manager
|
||||
- [deploy-rs](https://github.com/serokell/deploy-rs) - A simple multi-profile Nix-flake deploy tool.
|
||||
- [ffs](https://github.com/notgne2/ffs) - A tagging filesystem you can actually use
|
||||
- [DiscordBots](https://discordbots.org/) - Popular index of Discord bots and servers (now [Top.gg](https://top.gg))
|
||||
- [Tox](https://tox.chat) - FOSS Secure messaging application
|
||||
- [Continix](https://github.com/notgne2/continix) - Tool to use NixOS configurations to generate Docker images
|
||||
@ -20,6 +21,7 @@ I have worked significant amounts on various projects, most recently and/or nota
|
||||
## Contact me
|
||||
|
||||
- Matrix: [@notgne2:wizbos.club](https://matrix.to/#/@notgne2:wizbos.club)
|
||||
- Rizon/Freenode: [notgne2](#)
|
||||
- Libera IRC: [notgne2](#)
|
||||
- Email: [gen2@gen2.space](mailto:gen2@gen2.space)
|
||||
- Discord: [notgne2#0240](#)
|
||||
- Discord: [notgne2](#)
|
||||
|
||||
|
5
elm.json
5
elm.json
@ -6,11 +6,12 @@
|
||||
"elm-version": "0.19.1",
|
||||
"dependencies": {
|
||||
"direct": {
|
||||
"dillonkearns/elm-markdown": "1.1.3",
|
||||
"dillonkearns/elm-markdown": "7.0.1",
|
||||
"elm/browser": "1.0.2",
|
||||
"elm/core": "1.0.2",
|
||||
"elm/html": "1.0.0",
|
||||
"elm/http": "2.0.0",
|
||||
"elm/regex": "1.0.0",
|
||||
"elm/url": "1.0.0",
|
||||
"mdgriffith/elm-ui": "1.1.5"
|
||||
},
|
||||
@ -28,4 +29,4 @@
|
||||
"direct": {},
|
||||
"indirect": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
101
flake.lock
generated
101
flake.lock
generated
@ -16,19 +16,43 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat_2": {
|
||||
"flake": false,
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1606424373,
|
||||
"narHash": "sha256-oq8d4//CJOrVj+EcOaSXvMebvuTkmBJuT5tzlfewUnQ=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "99f1c2157fba4bfe6211a321fd0ee43199025dbf",
|
||||
"lastModified": 1660459072,
|
||||
"narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-npm-buildpackage": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1686315622,
|
||||
"narHash": "sha256-ccqZqY6wUFot0ewyNKQUrMR6IEliGza+pjKoSVMXIeM=",
|
||||
"owner": "serokell",
|
||||
"repo": "nix-npm-buildpackage",
|
||||
"rev": "991a792bccd611842f6bc1aa99fe80380ad68d44",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "serokell",
|
||||
"repo": "nix-npm-buildpackage",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
@ -48,28 +72,13 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pnpm2nix": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1613358015,
|
||||
"narHash": "sha256-ZitojlLnQkP+1Gy4kcuW6jPV2B9a8ce43LAYUnSsCZo=",
|
||||
"ref": "master",
|
||||
"rev": "460e356b5ea58b4e9780b49afc9e77f98d8d202b",
|
||||
"revCount": 91,
|
||||
"type": "git",
|
||||
"url": "ssh://git@github.com/notgne2/pnpm2nix.git"
|
||||
},
|
||||
"original": {
|
||||
"type": "git",
|
||||
"url": "ssh://git@github.com/notgne2/pnpm2nix.git"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"gitignore": "gitignore",
|
||||
"nix-npm-buildpackage": "nix-npm-buildpackage",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"utils": "utils",
|
||||
"wand": "wand"
|
||||
"utils": "utils"
|
||||
}
|
||||
},
|
||||
"utils": {
|
||||
@ -86,44 +95,6 @@
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"utils_2": {
|
||||
"locked": {
|
||||
"lastModified": 1610051610,
|
||||
"narHash": "sha256-U9rPz/usA1/Aohhk7Cmc2gBrEEKRzcW4nwPWMPwja4Y=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "3982c9903e93927c2164caa727cd3f6a0e6d14cc",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"wand": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat_2",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"pnpm2nix": "pnpm2nix",
|
||||
"utils": "utils_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1613358124,
|
||||
"narHash": "sha256-LDiOa7rSG7MyUAdpry1LUgmSpnEzFPEuQncc9bIE/Us=",
|
||||
"ref": "master",
|
||||
"rev": "dfd6e75545e054e4ec5b2c9bf0e48bd2f3bb6949",
|
||||
"revCount": 67,
|
||||
"type": "git",
|
||||
"url": "ssh://git@git.wizbos.club/wizbos-pub/wand-front-utils.git"
|
||||
},
|
||||
"original": {
|
||||
"type": "git",
|
||||
"url": "ssh://git@git.wizbos.club/wizbos-pub/wand-front-utils.git"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
107
flake.nix
107
flake.nix
@ -2,33 +2,102 @@
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
|
||||
wand.url = "git+ssh://git@git.wizbos.club/wizbos-pub/wand-front-utils.git";
|
||||
wand.inputs.nixpkgs.follows = "nixpkgs";
|
||||
|
||||
utils.url = "github:numtide/flake-utils";
|
||||
|
||||
nix-npm-buildpackage = {
|
||||
url = "github:serokell/nix-npm-buildpackage";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
gitignore = {
|
||||
url = "github:hercules-ci/gitignore.nix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
flake-compat = {
|
||||
url = "github:edolstra/flake-compat";
|
||||
flake = false;
|
||||
};
|
||||
};
|
||||
outputs = {
|
||||
self,
|
||||
nixpkgs,
|
||||
utils,
|
||||
nix-npm-buildpackage,
|
||||
gitignore,
|
||||
...
|
||||
}:
|
||||
utils.lib.eachDefaultSystem (system: let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
overlays = [nix-npm-buildpackage.overlays.default];
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, utils, wand, ... }:
|
||||
utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
in
|
||||
{
|
||||
packages.gen2space = wand.lib.${system}.mkWandardFront {
|
||||
src = ./.;
|
||||
name = "gen2.space";
|
||||
description = "notgne2's shitty pages";
|
||||
backgroundColor = "#220044";
|
||||
themeColor = "#004400";
|
||||
routes =
|
||||
[ "/" "/about" "/404" "/posts" "/posts/viveahk" "/posts/librebasics" ];
|
||||
src = gitignore.lib.gitignoreSource ./.;
|
||||
|
||||
nodeDependencies = with pkgs; [nasm zlib optipng libpng libtool automake autoconf vips pkgconfig python3 nodePackages.node-gyp];
|
||||
|
||||
buildVars = {
|
||||
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD = "1";
|
||||
PUPPETEER_EXECUTABLE_PATH = "${pkgs.chromium}/bin/chromium";
|
||||
};
|
||||
|
||||
preBuild = ''
|
||||
export LD=$CC
|
||||
'';
|
||||
in {
|
||||
packages.gen2-space-front-raw = pkgs.buildNpmPackage {
|
||||
inherit src;
|
||||
|
||||
dontNpmPrune = true;
|
||||
|
||||
extraNodeModulesArgs = {
|
||||
buildInputs = nodeDependencies;
|
||||
};
|
||||
|
||||
defaultPackage = self.packages.${system}.gen2space;
|
||||
});
|
||||
extraEnvVars = buildVars;
|
||||
};
|
||||
packages.gen2-space-front = pkgs.stdenv.mkDerivation ({
|
||||
name = "gen2-space-front";
|
||||
|
||||
src = self.packages.${system}.gen2-space-front-raw;
|
||||
|
||||
buildInputs = with pkgs; [nodejs elmPackages.elm];
|
||||
|
||||
configurePhase = pkgs.elmPackages.fetchElmDeps {
|
||||
elmVersion = "0.19.1";
|
||||
registryDat = ./registry.dat;
|
||||
elmPackages = import ./elm-srcs.nix;
|
||||
};
|
||||
|
||||
buildPhase = ''
|
||||
mkdir dist
|
||||
node $src/node_modules/vite/bin/vite.js build
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
mv dist $out
|
||||
'';
|
||||
}
|
||||
// buildVars);
|
||||
packages.default = self.packages.${system}.gen2-space-front;
|
||||
|
||||
devShells.default = pkgs.mkShell ({
|
||||
inputsFrom = builtins.attrValues self.packages.${system};
|
||||
shellHook = ''
|
||||
export LD=$CC
|
||||
'';
|
||||
buildInputs =
|
||||
nodeDependencies
|
||||
++ (with pkgs; [
|
||||
elmPackages.elm
|
||||
elmPackages.elm-format
|
||||
elmPackages.elm-review
|
||||
|
||||
elm2nix
|
||||
nodejs
|
||||
]);
|
||||
}
|
||||
// buildVars);
|
||||
});
|
||||
}
|
||||
|
20
index.html
Normal file
20
index.html
Normal file
@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
|
||||
<style>
|
||||
</style>
|
||||
|
||||
<title></title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/index.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
66
index.js
Normal file
66
index.js
Normal file
@ -0,0 +1,66 @@
|
||||
import { Elm } from './src/Main.elm'
|
||||
|
||||
if (typeof window === 'undefined') {
|
||||
global.window = {};
|
||||
}
|
||||
|
||||
|
||||
function _check_webp_feature(feature, callback) {
|
||||
var kTestImages = {
|
||||
lossy: "UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA",
|
||||
lossless: "UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==",
|
||||
alpha: "UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==",
|
||||
};
|
||||
|
||||
var img = new Image();
|
||||
img.onload = function () {
|
||||
var result = (img.width > 0) && (img.height > 0);
|
||||
callback(feature, result);
|
||||
};
|
||||
|
||||
img.onerror = function () {
|
||||
callback(feature, false);
|
||||
};
|
||||
|
||||
img.src = "data:image/webp;base64," + kTestImages[feature];
|
||||
}
|
||||
|
||||
async function getWebpFeatures() {
|
||||
return (await Promise.all(["lossy", "lossless", "alpha"].map((f) => {
|
||||
return new Promise((resolve, _) => {
|
||||
_check_webp_feature(f, (_, s) => resolve(s ? f : null));
|
||||
})
|
||||
}))).filter(f => f !== null);
|
||||
}
|
||||
|
||||
; (async () => {
|
||||
const webpFeatures = await getWebpFeatures();
|
||||
|
||||
const images = {
|
||||
// icon: (new URL("/data/images/icon.png", import.meta.url)).href,
|
||||
// logo: (new URL("/data/images/logo.png", import.meta.url)).href,
|
||||
// banners: {
|
||||
// 1: (new URL("/data/images/banners/1.jpg", import.meta.url)).href,
|
||||
// },
|
||||
};
|
||||
|
||||
const lossyWebps = {
|
||||
};
|
||||
|
||||
// use lossless=true
|
||||
const losslessWebps = {
|
||||
|
||||
};
|
||||
|
||||
const allImages = {
|
||||
...images,
|
||||
...(webpFeatures.includes("lossless") ? losslessWebps : {}),
|
||||
...(webpFeatures.includes("lossy") ? lossyWebps : {}),
|
||||
};
|
||||
|
||||
const app = Elm.Main.init({
|
||||
flags: {
|
||||
images: allImages,
|
||||
}
|
||||
});
|
||||
})()
|
4376
package-lock.json
generated
Normal file
4376
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
43
package.json
43
package.json
@ -1,12 +1,33 @@
|
||||
{
|
||||
"dependencies": {},
|
||||
"name": "gen2-space",
|
||||
"version": "1.0.0",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "notgne2",
|
||||
"license": "AGPL-3.0",
|
||||
"description": ""
|
||||
}
|
||||
"name": "gen2-space",
|
||||
"description": "gen2.space",
|
||||
"version": "1.2.0",
|
||||
"color": "#004400",
|
||||
"displayName": "gen2.space",
|
||||
"author": "notgne2",
|
||||
"license": "UNLICENSED",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"watch": "vite watch",
|
||||
"build": "vite build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"sharp": "^0.32.1",
|
||||
"svgo": "^3.0.2",
|
||||
"vite": "^4.3.9",
|
||||
"vite-imagetools": "^5.0.4",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-elm": "^2.9.0-beta.1",
|
||||
"vite-plugin-html-config": "^1.0.11",
|
||||
"vite-plugin-pwa": "^0.15.1",
|
||||
"ws": "^8.13.0"
|
||||
},
|
||||
"routes": [
|
||||
"/"
|
||||
],
|
||||
"alias": {
|
||||
"process": {
|
||||
"global": "process"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
174
src/MParser.elm
174
src/MParser.elm
@ -1,19 +1,23 @@
|
||||
module MParser exposing (ezmd)
|
||||
|
||||
import Element exposing (..)
|
||||
import Element.Background
|
||||
import Element.Border
|
||||
import Element.Background as Background
|
||||
import Element.Border as Border
|
||||
import Element.Font as Font
|
||||
import Element.Input as Input
|
||||
import Html
|
||||
import Html.Attributes
|
||||
import Markdown.Block as Block exposing (Block, Inline, ListItem(..), Task(..))
|
||||
import Markdown.Html
|
||||
import Markdown.Parser
|
||||
import Markdown.Renderer
|
||||
import SiteComponents exposing (..)
|
||||
|
||||
|
||||
ezmd input =
|
||||
case markdownView input of
|
||||
Ok rendered ->
|
||||
Element.column [ Element.spacing 30 ] rendered
|
||||
Element.column [ Element.spacing 25 ] rendered
|
||||
|
||||
Err errors ->
|
||||
Element.text errors
|
||||
@ -24,104 +28,150 @@ markdownView markdown =
|
||||
markdown
|
||||
|> Markdown.Parser.parse
|
||||
|> Result.mapError (\error -> error |> List.map Markdown.Parser.deadEndToString |> String.join "\n")
|
||||
|> Result.andThen (Markdown.Parser.render renderer)
|
||||
|> Result.andThen (Markdown.Renderer.render renderer)
|
||||
|
||||
|
||||
renderer : Markdown.Parser.Renderer (Element msg)
|
||||
renderer : Markdown.Renderer.Renderer (Element msg)
|
||||
renderer =
|
||||
{ heading = heading
|
||||
, raw = Element.paragraph [ Element.spacing 15 ]
|
||||
, thematicBreak = Element.none
|
||||
, plain = Element.text
|
||||
, bold = \content -> Element.row [ Font.bold ] [ Element.text content ]
|
||||
, italic = \content -> Element.row [ Font.italic ] [ Element.text content ]
|
||||
, code = code
|
||||
, paragraph = paragraph [ spacing 10 ]
|
||||
, thematicBreak = none
|
||||
, text = text
|
||||
, strong = \content -> row [ Font.bold, Font.color basedBrightText ] [ paragraph [] content ]
|
||||
, emphasis = \content -> row [ Font.italic ] [ paragraph [] content ]
|
||||
, strikethrough = \content -> row [ Font.strike ] [ paragraph [] content ]
|
||||
, codeSpan = code
|
||||
, link =
|
||||
\{ destination } body ->
|
||||
(if List.member (String.left 1 destination) [ "#", "/" ] then
|
||||
Element.link
|
||||
link
|
||||
|
||||
else
|
||||
Element.newTabLink
|
||||
newTabLink
|
||||
)
|
||||
[ Element.htmlAttribute (Html.Attributes.style "display" "inline-flex") ]
|
||||
[ htmlAttribute (Html.Attributes.style "display" "inline-flex") ]
|
||||
{ url = destination
|
||||
, label = Element.paragraph [ Font.color basedGreen, Font.underline ] body
|
||||
, label = paragraph [ Font.color basedGreen, Font.underline ] body
|
||||
}
|
||||
|> Ok
|
||||
, hardLineBreak = Html.br [] [] |> Element.html
|
||||
, image =
|
||||
\image body ->
|
||||
Element.image [ Element.width (Element.maximum 700 Element.fill) ] { src = image.src, description = body }
|
||||
|> Ok
|
||||
, list =
|
||||
\i ->
|
||||
image [ width (maximum 700 fill) ] { src = i.src, description = i.alt }
|
||||
, blockQuote =
|
||||
\children ->
|
||||
row []
|
||||
[ el [ width (px 10), height fill, patternBack basedPurple ] none
|
||||
, paragraph
|
||||
[ padding 10
|
||||
, patternBack basedNavBack
|
||||
]
|
||||
children
|
||||
]
|
||||
, unorderedList =
|
||||
\items ->
|
||||
Element.column [ Element.spacing 15 ]
|
||||
column [ paddingXY 10 0, spacing 5 ]
|
||||
(items
|
||||
|> List.map
|
||||
(\itemBlocks ->
|
||||
Element.row [ Element.spacing 5 ]
|
||||
[ Element.el
|
||||
[ Element.alignTop ]
|
||||
(Element.text "•")
|
||||
, itemBlocks
|
||||
(\(ListItem task children) ->
|
||||
paragraph
|
||||
[ alignTop ]
|
||||
((case task of
|
||||
IncompleteTask ->
|
||||
Input.defaultCheckbox False
|
||||
|
||||
CompletedTask ->
|
||||
Input.defaultCheckbox True
|
||||
|
||||
NoTask ->
|
||||
text "•"
|
||||
)
|
||||
:: text " "
|
||||
:: children
|
||||
)
|
||||
)
|
||||
)
|
||||
, orderedList =
|
||||
\startingIndex items ->
|
||||
column [ spacing 5 ]
|
||||
(items
|
||||
|> List.indexedMap
|
||||
(\index itemBlocks ->
|
||||
paragraph [ spacing 5 ]
|
||||
[ paragraph [ alignTop ]
|
||||
(text (String.fromInt (index + startingIndex) ++ " ") :: itemBlocks)
|
||||
]
|
||||
)
|
||||
)
|
||||
, codeBlock = codeBlock
|
||||
, table = column []
|
||||
, tableHeader =
|
||||
column
|
||||
[ Font.bold
|
||||
, width fill
|
||||
, Font.center
|
||||
]
|
||||
, tableBody = column []
|
||||
, tableRow = row [ height fill, width fill ]
|
||||
, tableHeaderCell =
|
||||
\maybeAlignment children ->
|
||||
paragraph
|
||||
tableBorder
|
||||
children
|
||||
, tableCell =
|
||||
\maybeAlignment children ->
|
||||
paragraph
|
||||
tableBorder
|
||||
children
|
||||
, html = Markdown.Html.oneOf []
|
||||
}
|
||||
|
||||
|
||||
rawTextToId rawText =
|
||||
rawText
|
||||
|> String.toLower
|
||||
|> String.replace " " "-"
|
||||
tableBorder =
|
||||
[ Border.color basedGreen
|
||||
, Border.width 1
|
||||
, Border.solid
|
||||
, paddingXY 6 13
|
||||
, height fill
|
||||
]
|
||||
|
||||
|
||||
heading : { level : Int, rawText : String, children : List (Element msg) } -> Element msg
|
||||
heading { level, rawText, children } =
|
||||
let
|
||||
fontSize =
|
||||
case level of
|
||||
1 ->
|
||||
36
|
||||
niceHeader
|
||||
(case level of
|
||||
Block.H1 ->
|
||||
1
|
||||
|
||||
2 ->
|
||||
24
|
||||
Block.H2 ->
|
||||
2
|
||||
|
||||
_ ->
|
||||
20
|
||||
in
|
||||
paragraph
|
||||
[ Element.htmlAttribute (Html.Attributes.attribute "name" (rawTextToId rawText))
|
||||
, Element.htmlAttribute (Html.Attributes.id (rawTextToId rawText))
|
||||
, width fill
|
||||
, height shrink
|
||||
, padding 10
|
||||
, greenLines
|
||||
, Font.size fontSize
|
||||
]
|
||||
Block.H3 ->
|
||||
3
|
||||
|
||||
_ ->
|
||||
4
|
||||
)
|
||||
rawText
|
||||
children
|
||||
|
||||
|
||||
code : String -> Element msg
|
||||
code snippet =
|
||||
Element.el
|
||||
[ Element.Background.color (Element.rgba 0 0 0 0.04)
|
||||
el
|
||||
[ Background.color (rgba 0 0 0 0.04)
|
||||
, scrollbars
|
||||
, Element.Border.rounded 2
|
||||
, Element.paddingXY 5 3
|
||||
, Border.rounded 2
|
||||
, paddingXY 5 3
|
||||
]
|
||||
(Element.text snippet)
|
||||
(text snippet)
|
||||
|
||||
|
||||
codeBlock : { body : String, language : Maybe String } -> Element msg
|
||||
codeBlock details =
|
||||
Element.el
|
||||
[ Element.Background.color (Element.rgba 0 0 0 0.03)
|
||||
el
|
||||
[ Background.color (rgba 0 0 0 0.03)
|
||||
, scrollbars
|
||||
, Element.htmlAttribute (Html.Attributes.style "white-space" "pre")
|
||||
, Element.padding 20
|
||||
, Element.width Element.fill
|
||||
, htmlAttribute (Html.Attributes.style "white-space" "pre")
|
||||
, padding 20
|
||||
, width fill
|
||||
]
|
||||
(Element.text details.body)
|
||||
(text details.body)
|
||||
|
26
src/Main.elm
26
src/Main.elm
@ -4,6 +4,7 @@ import Browser
|
||||
import Browser.Navigation as Nav
|
||||
import Dict exposing (Dict)
|
||||
import Element exposing (..)
|
||||
import Element.Background as Background
|
||||
import Element.Font as Font
|
||||
import Http
|
||||
import List
|
||||
@ -159,6 +160,7 @@ view model =
|
||||
, Font.typeface "monospace"
|
||||
, Font.monospace
|
||||
]
|
||||
, Font.size 16
|
||||
]
|
||||
(page model)
|
||||
]
|
||||
@ -169,7 +171,7 @@ navbarHeader =
|
||||
link
|
||||
[ centerX
|
||||
, centerY
|
||||
, Font.size 30
|
||||
, Font.size 35
|
||||
]
|
||||
{ url = "/", label = image [ width fill ] { src = "/data/images/icon.png", description = "gen2 space" } }
|
||||
|
||||
@ -178,7 +180,7 @@ navbarLinks =
|
||||
List.map
|
||||
(\pageLink ->
|
||||
link
|
||||
[ Font.color basedPurple ]
|
||||
[ Font.color basedGreen ]
|
||||
{ url = "/" ++ pageLink
|
||||
, label = text ("/" ++ pageLink)
|
||||
}
|
||||
@ -196,9 +198,9 @@ navbarLinks =
|
||||
navbar =
|
||||
column
|
||||
[ width fill
|
||||
, greenLines
|
||||
, spacing 10
|
||||
, padding 10
|
||||
, patternBack basedNavBack
|
||||
]
|
||||
[ navbarHeader, navbarLinks ]
|
||||
|
||||
@ -207,11 +209,10 @@ page model =
|
||||
column
|
||||
[ width fill
|
||||
, height fill
|
||||
, linesBackground "#222222"
|
||||
, spacing 20
|
||||
, patternBack basedPurple
|
||||
]
|
||||
[ navbar
|
||||
, body model
|
||||
, el [ paddingXY 20 0, width fill, height fill ] (body model)
|
||||
]
|
||||
|
||||
|
||||
@ -224,7 +225,7 @@ posts =
|
||||
|
||||
|
||||
postPreview ( name, data ) =
|
||||
textColumn [ width fill, spacing 15 ] [ link [ Font.color basedPurple ] { url = "/posts/" ++ name, label = niceHeader ("Post: " ++ name) }, paragraph [] [ text data.subtext ] ]
|
||||
textColumn [ width fill, spacing 15 ] [ link [] { url = "/posts/" ++ name, label = basicNiceHeader 2 ("Post: " ++ name) }, paragraph [] [ text data.subtext ] ]
|
||||
|
||||
|
||||
postsText =
|
||||
@ -243,15 +244,16 @@ postText model name =
|
||||
notFoundLines
|
||||
|
||||
Just data ->
|
||||
niceBigHeader name :: [ ezmd (Maybe.withDefault "" (Dict.get ("posts/" ++ name) model.sources)) ]
|
||||
basicNiceHeader 1 name :: [ ezmd (Maybe.withDefault "" (Dict.get ("posts/" ++ name) model.sources)) ]
|
||||
|
||||
|
||||
body model =
|
||||
el
|
||||
[ linesBackground "#220044"
|
||||
, Font.color (rgb255 255 255 255)
|
||||
, padding 30
|
||||
, width (maximum 1300 fill)
|
||||
[ Background.color basedBackground
|
||||
, Font.color basedText
|
||||
, paddingEach { top = 20, left = 20, bottom = 80, right = 20 }
|
||||
, width (maximum 1200 fill)
|
||||
, height fill
|
||||
, centerX
|
||||
]
|
||||
(textColumn [ width fill, spacing 15 ]
|
||||
|
@ -1,8 +1,9 @@
|
||||
module SiteComponents exposing (basedGreen, basedPurple, edges, greenLines, linesBackground, niceBigHeader, niceHeader)
|
||||
module SiteComponents exposing (basedBackground, basedBrightText, basedGreen, basedNavBack, basedPurple, basedText, basicNiceHeader, edges, greenLines, niceHeader, patternBack, rawTextToId)
|
||||
|
||||
import Element exposing (..)
|
||||
import Element.Font as Font
|
||||
import Html.Attributes
|
||||
import Regex
|
||||
|
||||
|
||||
edges =
|
||||
@ -14,38 +15,95 @@ edges =
|
||||
|
||||
|
||||
basedPurple =
|
||||
rgb255 40 0 90
|
||||
rgb255 15 0 40
|
||||
|
||||
|
||||
basedGreen =
|
||||
rgb255 20 100 20
|
||||
rgb255 5 150 5
|
||||
|
||||
|
||||
linesBackground cssColor =
|
||||
htmlAttribute (Html.Attributes.style "background" ("repeating-linear-gradient(" ++ cssColor ++ ", " ++ cssColor ++ " 1px, #2F2F2F 1px, #2F2F2F 2px)"))
|
||||
basedBrightText =
|
||||
rgb255 255 255 255
|
||||
|
||||
|
||||
basedText =
|
||||
rgb255 240 240 240
|
||||
|
||||
|
||||
basedBackground =
|
||||
rgb255 28 28 28
|
||||
|
||||
|
||||
basedNavBack =
|
||||
rgb255 20 20 20
|
||||
|
||||
|
||||
patternBack color =
|
||||
htmlAttribute <|
|
||||
Html.Attributes.style "background"
|
||||
(let
|
||||
c =
|
||||
toRgb color
|
||||
|
||||
cs cc =
|
||||
String.fromInt (round (cc * 255))
|
||||
|
||||
fc =
|
||||
"rgb(" ++ cs c.red ++ ", " ++ cs c.green ++ ", " ++ cs c.blue ++ ")"
|
||||
in
|
||||
"repeating-linear-gradient(90deg, " ++ fc ++ ", " ++ fc ++ "1px, #000 1px, #000 2px)"
|
||||
)
|
||||
|
||||
|
||||
greenLines =
|
||||
linesBackground "#004400"
|
||||
patternBack (rgb255 0 90 0)
|
||||
|
||||
|
||||
niceHeader headerText =
|
||||
el
|
||||
[ width fill
|
||||
rawTextToId rawText =
|
||||
(case Regex.fromString "^[^a-zA-Z]|[^a-zA-Z0-9_:. -]" of
|
||||
Nothing ->
|
||||
rawText
|
||||
|
||||
Just regex ->
|
||||
Regex.replace regex (\_ -> "") rawText
|
||||
)
|
||||
|> String.toLower
|
||||
|> String.replace " " "-"
|
||||
|
||||
|
||||
niceHeader level rawText children =
|
||||
let
|
||||
fontSize =
|
||||
case level of
|
||||
1 ->
|
||||
36
|
||||
|
||||
2 ->
|
||||
24
|
||||
|
||||
3 ->
|
||||
20
|
||||
|
||||
_ ->
|
||||
15
|
||||
in
|
||||
paragraph
|
||||
[ width
|
||||
(if level < 4 then
|
||||
fill
|
||||
|
||||
else
|
||||
shrink
|
||||
)
|
||||
, height shrink
|
||||
, padding 10
|
||||
, Font.size 24
|
||||
, Font.size fontSize
|
||||
, greenLines
|
||||
, Element.htmlAttribute (Html.Attributes.attribute "name" (rawTextToId rawText))
|
||||
, Element.htmlAttribute (Html.Attributes.id (rawTextToId rawText))
|
||||
]
|
||||
(text headerText)
|
||||
children
|
||||
|
||||
|
||||
niceBigHeader headerText =
|
||||
el
|
||||
[ width fill
|
||||
, height shrink
|
||||
, padding 10
|
||||
, Font.size 30
|
||||
, greenLines
|
||||
]
|
||||
(text headerText)
|
||||
basicNiceHeader size t =
|
||||
niceHeader size t [ text t ]
|
||||
|
@ -1,7 +0,0 @@
|
||||
const { Elm } = require('./Main.elm');
|
||||
|
||||
Elm.Main.init({});
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', () => navigator.serviceWorker.register('/sw.js'));
|
||||
}
|
243
vite.config.js
Normal file
243
vite.config.js
Normal file
@ -0,0 +1,243 @@
|
||||
import { defineConfig } from "vite";
|
||||
|
||||
import elmPlugin from "vite-plugin-elm";
|
||||
import { VitePWA } from "vite-plugin-pwa"
|
||||
import viteCompression from "vite-plugin-compression"
|
||||
// import vitePrerender from "vite-plugin-prerender"
|
||||
// import prerender from '@prerenderer/rollup-plugin'
|
||||
import { imagetools } from "vite-imagetools"
|
||||
import htmlPlugin from "vite-plugin-html-config"
|
||||
|
||||
import { promises as fs } from "fs"
|
||||
import path from "path"
|
||||
|
||||
// const Renderer = vitePrerender.PuppeteerRenderer;
|
||||
|
||||
/** @type {import("vite").UserConfig} */
|
||||
export default defineConfig(async () => {
|
||||
const pkg = JSON.parse(await fs.readFile(path.join(__dirname, "package.json")));
|
||||
|
||||
const lossyQuality = 85;
|
||||
const prerenderOptions = {
|
||||
staticDir: path.join(__dirname, "dist"),
|
||||
routes: pkg.routes,
|
||||
minify: {
|
||||
collapseBooleanAttributes: true,
|
||||
collapseInlineTagWhitespace: true,
|
||||
collapseWhitespace: true,
|
||||
decodeEntities: true,
|
||||
sortAttributes: true,
|
||||
sortClassName: true,
|
||||
minifyCSS: true,
|
||||
minifyJS: true,
|
||||
removeAttributeQuotes: true,
|
||||
removeComments: true,
|
||||
removeScriptTypeAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
removeRedundantAttributes: true,
|
||||
removeOptionalTags: true,
|
||||
},
|
||||
};
|
||||
|
||||
const host = process.env.URL || import.meta.url || "http://localhost";
|
||||
const getURL = (path = "") => new URL(path, host).toString();
|
||||
const getPath = (path = "") => {
|
||||
const x = new URL(path, host);
|
||||
return x.pathname + x.search;
|
||||
// return await import("./" + path);
|
||||
};
|
||||
|
||||
return {
|
||||
plugins: [
|
||||
htmlPlugin({
|
||||
title: pkg.displayName,
|
||||
|
||||
// TODO .ico
|
||||
favicon: getPath("/data/images/icon.png?h=16&w=16"),
|
||||
|
||||
links: [
|
||||
{
|
||||
rel: "apple-touch-icon",
|
||||
href: getPath("/data/images/icon.png?h=180&w=180"),
|
||||
sizes: "180x180",
|
||||
},
|
||||
{
|
||||
rel: "icon",
|
||||
href: getPath("/data/images/icon.png?h=32&w=32"),
|
||||
sizes: "32x32",
|
||||
type: "image/png",
|
||||
},
|
||||
{
|
||||
rel: "icon",
|
||||
href: getPath("/data/images/icon.png?h=16&w=16"),
|
||||
sizes: "16x16",
|
||||
type: "image/png",
|
||||
},
|
||||
{
|
||||
rel: "mask-icon",
|
||||
href: getPath("/data/images/icon.png?h=180&w=180"),
|
||||
sizes: "180x180",
|
||||
color: pkg.color,
|
||||
},
|
||||
{
|
||||
rel: "icon",
|
||||
href: getPath("/data/images/icon.png?h=16&w=16"),
|
||||
sizes: "16x16",
|
||||
},
|
||||
// TODO .ico
|
||||
{
|
||||
rel: "icon",
|
||||
href: getPath("/data/images/icon.png?h=16&w=16"),
|
||||
sizes: "16x16",
|
||||
},
|
||||
],
|
||||
|
||||
metas: [
|
||||
{
|
||||
name: "description",
|
||||
content: pkg.description,
|
||||
},
|
||||
{
|
||||
name: "author",
|
||||
content: pkg.author.name,
|
||||
},
|
||||
{
|
||||
name: "theme-color",
|
||||
content: pkg.color,
|
||||
},
|
||||
{
|
||||
name: "og:url",
|
||||
content: host,
|
||||
},
|
||||
{
|
||||
name: "og:type",
|
||||
content: "website",
|
||||
},
|
||||
{
|
||||
name: "og:title",
|
||||
content: pkg.displayName,
|
||||
},
|
||||
{
|
||||
name: "og:description",
|
||||
content: pkg.description,
|
||||
},
|
||||
{
|
||||
name: "og:image",
|
||||
content: getURL("/data/images/logo.png"),
|
||||
},
|
||||
{
|
||||
name: "twitter:card",
|
||||
content: "summary_large_image",
|
||||
},
|
||||
{
|
||||
name: "twitter:domain",
|
||||
content: "",
|
||||
},
|
||||
{
|
||||
name: "twitter:url",
|
||||
content: host,
|
||||
},
|
||||
{
|
||||
name: "twitter:title",
|
||||
content: pkg.displayName,
|
||||
},
|
||||
{
|
||||
name: "twitter:description",
|
||||
content: pkg.description,
|
||||
},
|
||||
{
|
||||
name: "twitter:image",
|
||||
content: getURL("/data/images/logo.png"),
|
||||
},
|
||||
],
|
||||
}),
|
||||
elmPlugin.plugin(),
|
||||
VitePWA({
|
||||
injectRegister: "auto",
|
||||
registerType: "autoUpdate",
|
||||
manifest: {
|
||||
name: pkg.displayName,
|
||||
// TODO better?
|
||||
short_name: pkg.displayName,
|
||||
description: pkg.description,
|
||||
theme_color: pkg.color,
|
||||
icons: [
|
||||
{
|
||||
src: getPath("/data/images/icon.png?h=192&w=192"),
|
||||
sizes: "192x192",
|
||||
type: "image/png",
|
||||
},
|
||||
{
|
||||
src: getPath("/data/images/icon.png?h=512&w=512"),
|
||||
sizes: "512x512",
|
||||
type: "image/png",
|
||||
},
|
||||
{
|
||||
src: getPath("/data/images/icon.png?h=512&w=512"),
|
||||
sizes: "512x512",
|
||||
type: "image/png",
|
||||
purpose: "any maskable",
|
||||
},
|
||||
]
|
||||
},
|
||||
devOptions: {
|
||||
enabled: true
|
||||
}
|
||||
}),
|
||||
imagetools({
|
||||
defaultDirectives: (url) => {
|
||||
if (url.pathname.endsWith(".png")) {
|
||||
return new URLSearchParams({
|
||||
format: "webp",
|
||||
lossless: true,
|
||||
quality: 100,
|
||||
});
|
||||
}
|
||||
|
||||
if (url.pathname.endsWith(".l.png")) {
|
||||
return new URLSearchParams({
|
||||
format: "webp",
|
||||
quality: lossyQuality,
|
||||
});
|
||||
}
|
||||
|
||||
if (url.pathname.endsWith(".jpg")) {
|
||||
return new URLSearchParams({
|
||||
format: "webp",
|
||||
quality: lossyQuality,
|
||||
});
|
||||
}
|
||||
|
||||
return new URLSearchParams();
|
||||
},
|
||||
}),
|
||||
viteCompression({
|
||||
algorithm: "brotliCompress",
|
||||
}),
|
||||
// vitePrerender({
|
||||
// ...prerenderOptions,
|
||||
// outputDir: path.join(__dirname, "dist", "prerender", "mobile"),
|
||||
// renderer: new Renderer({
|
||||
// viewport: {
|
||||
// isMobile: true,
|
||||
// hasTouch: true,
|
||||
// width: 450,
|
||||
// height: 800,
|
||||
// },
|
||||
// }),
|
||||
// }),
|
||||
// vitePrerender({
|
||||
// ...prerenderOptions,
|
||||
// outputDir: path.join(__dirname, "dist", "prerender", "desktop"),
|
||||
// renderer: new Renderer({
|
||||
// viewport: {
|
||||
// isMobile: false,
|
||||
// hasTouch: false,
|
||||
// width: 1500,
|
||||
// height: 1000,
|
||||
// },
|
||||
// }),
|
||||
// }),
|
||||
],
|
||||
}
|
||||
});
|
4
wand.js
4
wand.js
@ -1,4 +0,0 @@
|
||||
document.body.innerHTML = ''
|
||||
const sourceTarget = document.createElement('script')
|
||||
sourceTarget.src = '/main.js'
|
||||
document.body.appendChild(sourceTarget)
|
@ -1,191 +0,0 @@
|
||||
const ROUTES = ["/","/about","/404","/posts","/posts/viveahk","/posts/librebasics"]
|
||||
const PRETTY_NAME = "gen2.space"
|
||||
const DESCRIPTION = "notgne2's shitty pages"
|
||||
const THEME_COLOR = "#004400"
|
||||
const BACKGROUND_COLOR = "#220044"
|
||||
|
||||
const path = require('path')
|
||||
const TerserPlugin = require('terser-webpack-plugin')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const CopyPlugin = require('copy-webpack-plugin')
|
||||
const { InjectManifest } = require('workbox-webpack-plugin')
|
||||
const ImageminPlugin = require('imagemin-webpack-plugin').default
|
||||
const ImageminWebpWebpackPlugin = require('imagemin-webp-webpack-plugin')
|
||||
const FaviconsWebpackPlugin = require('favicons-webpack-plugin')
|
||||
const { JSDOM } = require('jsdom')
|
||||
const tempy = require('tempy')
|
||||
const fs = require('fs')
|
||||
const webpack = require('webpack')
|
||||
|
||||
const magicFile = (text) => {
|
||||
const p = tempy.file()
|
||||
fs.writeFileSync(p, text)
|
||||
return p
|
||||
}
|
||||
|
||||
const jsMin = {
|
||||
mangle: true,
|
||||
compress: {
|
||||
keep_fargs: false,
|
||||
pure_getters: true,
|
||||
pure_funcs: ['F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9'],
|
||||
unsafe: true,
|
||||
ecma: 6,
|
||||
unsafe_comps: true,
|
||||
unsafe_arrows: true,
|
||||
}
|
||||
}
|
||||
|
||||
const htmlMin = {
|
||||
minifyCSS: {
|
||||
compatibility: 'ie9',
|
||||
level: 2
|
||||
},
|
||||
minifyJS: jsMin,
|
||||
|
||||
collapseBooleanAttributes: true,
|
||||
removeRedundantAttributes: true,
|
||||
removeScriptTypeAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
|
||||
removeComments: true,
|
||||
sortAttributes: true,
|
||||
sortClassName: true,
|
||||
}
|
||||
|
||||
const productionPlugins = [
|
||||
new InjectManifest({
|
||||
importWorkboxFrom: 'local',
|
||||
swSrc: magicFile(`
|
||||
workbox.core.skipWaiting();
|
||||
workbox.core.clientsClaim();
|
||||
workbox.precaching.cleanupOutdatedCaches()
|
||||
workbox.routing.registerNavigationRoute('/index.html');
|
||||
workbox.precaching.precacheAndRoute(self.__precacheManifest);
|
||||
`),
|
||||
swDest: 'sw.js',
|
||||
}),
|
||||
new ImageminPlugin({
|
||||
jpegtran: { progressive: true },
|
||||
svgo: null,
|
||||
}),
|
||||
new ImageminWebpWebpackPlugin({
|
||||
config: [{
|
||||
test: /\.jpe?g/,
|
||||
options: {
|
||||
force: true,
|
||||
quality: 75
|
||||
}
|
||||
}, {
|
||||
test: /\.l\.png/,
|
||||
options: {
|
||||
force: true,
|
||||
quality: 75
|
||||
}
|
||||
}, {
|
||||
test: /\.png/,
|
||||
options: {
|
||||
force: true,
|
||||
lossless: true
|
||||
}
|
||||
}],
|
||||
overrideExtension: false,
|
||||
detailedLogs: false,
|
||||
silent: true,
|
||||
strict: true,
|
||||
}),
|
||||
new FaviconsWebpackPlugin({
|
||||
logo: path.join(__dirname, 'data', 'images', 'icon.png'),
|
||||
inject: false, // manually in our template for now cos its fuckd
|
||||
mode: 'webapp',
|
||||
prefix: 'appdata',
|
||||
favicons: {
|
||||
appName: PRETTY_NAME,
|
||||
appDescription: DESCRIPTION,
|
||||
background: BACKGROUND_COLOR,
|
||||
theme_color: THEME_COLOR,
|
||||
}
|
||||
}),
|
||||
];
|
||||
|
||||
let plugins = (env, argv) => [
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
new CopyPlugin([{ from: 'data', to: 'data' }, { from: 'wand.js', to: 'wand.js' }]),
|
||||
new HtmlWebpackPlugin({
|
||||
hash: true,
|
||||
inject: true,
|
||||
minify: argv.mode == 'production' ? htmlMin : false,
|
||||
template: './template.html',
|
||||
meta: {
|
||||
viewport: 'width=320, initial-scale=1, maximum-scale=5',
|
||||
description: DESCRIPTION,
|
||||
}
|
||||
}),
|
||||
];
|
||||
|
||||
module.exports = (env, argv) => ({
|
||||
target: 'web',
|
||||
externals: {
|
||||
svgo: 'svgo'
|
||||
},
|
||||
entry: magicFile(`
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', () => navigator.serviceWorker.register('/sw.js'));
|
||||
}
|
||||
|
||||
require('${process.env.PWD}/src/index.js')
|
||||
`),
|
||||
output: {
|
||||
filename: 'main.js',
|
||||
path: path.join(__dirname, 'dist'),
|
||||
publicPath: '/'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.elm$/,
|
||||
exclude: [/elm-stuff/, /node_modules/],
|
||||
use: [
|
||||
{
|
||||
loader: 'elm-hot-webpack-loader',
|
||||
options: {
|
||||
debug: argv.mode != 'production',
|
||||
}
|
||||
},
|
||||
{
|
||||
loader: 'elm-webpack-loader',
|
||||
options: {
|
||||
optimize: argv.mode == 'production',
|
||||
debug: argv.mode != 'production',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.html$/,
|
||||
loader: 'html-loader',
|
||||
options: {
|
||||
minimize: true,
|
||||
...htmlMin
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: ['style-loader', 'css-loader'],
|
||||
},
|
||||
],
|
||||
},
|
||||
devServer: {
|
||||
inline: true,
|
||||
hot: true,
|
||||
stats: 'errors-only'
|
||||
},
|
||||
optimization: {
|
||||
minimize: argv.mode == 'production',
|
||||
minimizer: [
|
||||
new TerserPlugin({ terserOptions: jsMin }),
|
||||
new TerserPlugin({ terserOptions: jsMin }),
|
||||
],
|
||||
},
|
||||
plugins: [...plugins(env, argv), ...(argv.mode == 'production' ? productionPlugins : [])],
|
||||
})
|
Loading…
Reference in New Issue
Block a user