feat: custom static site generation

This commit is contained in:
hippoz 2021-10-17 00:05:22 +03:00
parent dc8e9b73f5
commit 88be65e0a6
No known key found for this signature in database
GPG key ID: 7C52899193467641
23 changed files with 635 additions and 312 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
node_modules/
# the only files that need to be generated are .html, so just exclude them
out/*.html

9
LICENSE Normal file
View file

@ -0,0 +1,9 @@
MIT License
Copyright (c) 2021 hippoz
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -1,2 +1,26 @@
# homepage # homepage
This repository holds the entire source code of my website. It has a custom static site generation system.
## Building
NPM/Yarn and Node are required.
```
npm install
npm start
```
This will "build" all of the necessary html files and place them into the `out` folder. The `out` folder now contains all the files necessary for the website, so it can be served somewhere.
## Structure
`src/components/` - Contains reusable components.
`src/env/` - Contains configuration for how the website should be laid out/where it should get data from.
`src/*.page.js` - Files that end in .page.js will be "built" into html files. They must export a string.
`buildconfig.js` - Contains various parameters that alter the build process.
`out/` - All generated html files will be placed in this folder. Note that by default it contains certain static content such as the css styles.

View file

@ -1,32 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="hippoz personal blog">
<title>hippoz blog</title>
<link rel="stylesheet" href="res/style.css">
</head>
<body class="wave-background">
<div class="card layout-card navigation noselect">
<div class="navigation-branding">
<b class="navigation-branding-text">hippoz.</b>
</div>
<div class="navigation-buttons">
<a href="index.html" class="button-default">home</a>
<a href="blog.html" class="button-default button-selected">blog</a>
<a href="https://git.hippoz.xyz" class="button-default">git</a>
<a href="me.html" class="button-default">me</a>
</div>
</div>
<div class="card layout-card">
<h2>hippoz's blog</h2>
<p>i think</p>
<br>
<br>
<p>no articles yet</p>
</div>
</body>
</html>

96
build.js Normal file
View file

@ -0,0 +1,96 @@
const { promises: fs } = require("fs");
const path = require("path");
const { html: beautifyHtml } = require('js-beautify');
const buildConfig = require("./buildconfig");
// source: https://stackoverflow.com/a/41407246
const escapeCodes = {
reset: "\x1b[0m",
bright: "\x1b[1m",
dim: "\x1b[2m",
underscore: "\x1b[4m",
blink: "\x1b[5m",
reverse: "\x1b[7m",
hidden: "\x1b[8m",
fgBlack: "\x1b[30m",
fgRed: "\x1b[31m",
fgGreen: "\x1b[32m",
fgYellow: "\x1b[33m",
fgBlue: "\x1b[34m",
fgMagenta: "\x1b[35m",
fgCyan: "\x1b[36m",
fgWhite: "\x1b[37m",
bgBlack: "\x1b[40m",
bgRed: "\x1b[41m",
bgGreen: "\x1b[42m",
bgYellow: "\x1b[43m",
bgBlue: "\x1b[44m",
bgMagenta: "\x1b[45m",
bgCyan: "\x1b[46m",
bgWhite: "\x1b[47m",
};
if (!buildConfig.allowSpecialCharacters) {
for (i in escapeCodes) {
escapeCodes[i] = "";
}
}
const specialCharacter = (c) => {
if (buildConfig.allowSpecialCharacters) {
return c;
}
return "";
}
const processPageOutput = (out) => {
if (typeof out !== "string") {
throw new Error("got non-string page output (maybe one of the pages isn't exporting a string?)");
}
if (buildConfig.postProcessing.trimOutput) {
out = out.trim();
}
if (buildConfig.postProcessing.beautifyOutput) {
out = beautifyHtml(out, buildConfig.postProcessing.beautifyOutputOptions);
}
return out;
};
const findAllPages = async (directory) =>
(await fs.readdir(directory))
.filter(
f => f.endsWith(buildConfig.pageExtension)
)
.map(f => directory + "/" + f); // TODO: hack
const buildPage = (pagePath) => processPageOutput(require(pagePath));
const buildAllPages = async (directory) =>
(await findAllPages(directory))
.map(
p => [p, buildPage(p)]
);
const exportAllPages = async (sourcePath, outputPath) => {
const pages = await buildAllPages(sourcePath);
for (const [pagePath, pageContent] of pages) {
const pageName = path.parse(path.basename(pagePath, buildConfig.pageExtension)).name;
await fs.writeFile(path.join(outputPath, pageName + ".html"), pageContent);
console.log(`${escapeCodes.fgGreen}${specialCharacter("→")}${escapeCodes.reset} Built ${escapeCodes.fgBlue}${pageName}${escapeCodes.reset}`);
}
return pages.length;
};
const main = async () => {
const startTime = new Date();
const builtPages = await exportAllPages(buildConfig.sourceDirectory, buildConfig.outputDirectory);
console.log(`${escapeCodes.fgGreen}${specialCharacter("✓")} Done! ${escapeCodes.fgBlue}Built ${builtPages} pages in ${((new Date()) - startTime)}ms.${escapeCodes.reset}`);
};
main();

14
buildconfig.js Normal file
View file

@ -0,0 +1,14 @@
module.exports = {
sourceDirectory: "./src",
outputDirectory: "./out",
pageExtension: ".page.js",
postProcessing: {
trimOutput: false, // not very useful when beautifyOutput === true
beautifyOutput: true,
beautifyOutputOptions: {
indent_size: 2,
preserve_newlines: false
}
},
allowSpecialCharacters: true
};

View file

@ -1,144 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>hippoz</title>
<link rel="stylesheet" href="https://hippoz.xyz/res/style.css">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div class="card navigation-card">
<div class="navigation-buttons">
<a v-for="(webhook, webhookIndex) in webhooks" @click="selectedWebhook = webhookIndex" :class="getWebhookButtonClass(webhookIndex)">{{ webhook.name }}</a>
</div>
</div>
<div class="card" id="content-container">
<h2>Selected webhook: {{ selectedWebhookObject.name }}</h2>
<p>i think</p>
<br>
<div id="contols" v-if="selectedWebhookObject.url && selectedWebhookObject.name">
<input class="input" placeholder="message" type="text" v-model="message" v-on:keyup.enter="sendMessage(selectedWebhookObject, message)"> </br>
<button class="button-default button-selected" @click="sendMessage(selectedWebhookObject, message)">Send</button>
<br>
<button class="button-default button-selected" @click="deleteCurrentWebhook()">Delete</button>
</div>
<br>
<div>
<button class="button-default button-selected" @click="createWebhook()">New webhook</button>
</div>
</div>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
webhooks: [
{
name: 'test',
url: 'https://discordapp.com/api/webhooks/765640620327829525/Qe9525MFu14-K_t7Fq3PitanumcF-gLf_NZ9ncG0RD2yPye9JEkuC3L7vkDqxRtvvQMD'
}
],
selectedWebhook: -1,
message: ''
},
mounted: function() {
this.loadFromLocalstorage();
},
methods: {
sendMessage: async function(webhook, message) {
if (!webhook.url || !webhook.name) {
alert('Invalid webhook does not have url or name property');
return;
}
if (!message) {
alert('Message is required');
return
}
const res = await fetch(webhook.url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
content: message
})
});
if (res.status !== 204) {
alert(`Request failed with status code ${res.status} and content "${await res.text()}" (POST ${webhook.url})`);
return;
}
},
createWebhook: function() {
const name = prompt('Webhook name');
const url = prompt('Webhook url');
if (!url || !name || url === '' || name === '') {
alert('Invalid input');
return;
}
this.webhooks.push({ name, url });
this.saveToLocalstorage();
alert(`Webhook "${name}" successfully created`);
},
deleteWebhookByIndex: function(index) {
this.webhooks.splice(index, 1);
},
deleteCurrentWebhook: function() {
const yn = confirm(`Delete webhook ${this.selectedWebhookObject.name}?`);
if (yn) {
this.deleteWebhookByIndex(this.selectedWebhook);
this.selectedWebhook = -1;
this.saveToLocalstorage();
alert(`Webhook successfully deleted`);
}
},
saveToLocalstorage: function() {
localStorage.setItem('webhooks', JSON.stringify(this.webhooks));
},
loadFromLocalstorage: function() {
const savedWebhooks = localStorage.getItem('webhooks');
if (!savedWebhooks) {
this.webhooks = [];
return;
}
try {
const webhooksJSON = JSON.parse(savedWebhooks);
if (!webhooksJSON) {
alert('Parsing localstorage data returned undefined, clearing localstorage...', e);
this.webhooks = [];
localStorage.clear();
return;
}
this.webhooks = webhooksJSON;
} catch(e) {
alert('Error while parsing saved localstorage data, clearing localstorage...', e);
this.webhooks = [];
localStorage.clear();
return;
}
},
getWebhookButtonClass: function(webhookIndex) {
if (this.selectedWebhook === webhookIndex) {
return 'button-default button-selected';
}
return 'button-default';
}
},
computed: {
selectedWebhookObject: function() {
return this.webhooks[this.selectedWebhook] || { name: 'none' };
}
}
});
</script>
</body>
</html>

View file

@ -1,56 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="hippoz personal website homepage">
<title>extra</title>
<link rel="stylesheet" href="/res/style.css">
<script>
const init = () => {
const displayExtra = localStorage.getItem("__DISPLAY_EXTRA");
if (displayExtra !== "oksure") document.write("you probably should not be here (you can be, though)");
};
</script>
</head>
<body class="wave-background" onload="init();">
<div class="card layout-card navigation noselect">
<div class="navigation-branding">
<b class="navigation-branding-text">diag.</b>
</div>
<div class="navigation-buttons">
<a href="/" class="button-default">back to /</a>
</div>
</div>
<div class="card layout-card">
<h2>diag</h2>
<p>i think.</p>
<h4>STATIC @ /</h3>
<ul>
<li><a href="/">root</a><br>(/)<br></li>
<li><a href="/blog.html">blog</a><br>(/blog.html)<br></li>
<li><a href="/me.html">me</a><br>(/me.html)<br></li>
<li><a href="/favicon.ico">favicon</a><br>(/favicon.ico)<br></li>
<li><a href="/res">res</a><br>(/res)<br></li>
<li><a href="/res/style.css">main stylesheet</a><br>(/res/style.css)<br></li>
<li><a href="/res/wave.svg">wave image</a><br>(/res/wave.svg)<br></li>
<li><a href="/extra/">diag</a><br>(/extra/)<br></li>
<li><a href="/extra/discordwebhookmanager/">webhook manager</a><br>(/extra/discordwebhookmanager/)<br></li>
</ul>
<h4>SERVICES @ hippoz.xyz</h3>
<ul>
<li><a href="https://hippoz.xyz/">www root</a><br>(https://hippoz.xyz/)<br></li>
<li><a href="https://git.hippoz.xyz/">git</a><br>(https://git.hippoz.xyz/)<br></li>
<li><a href="https://provider.hippoz.xyz/">content delivery</a><br>(https://provider.hippoz.xyz/)<br></li>
<li><a href="https://b.hippoz.xyz/">staging development INTERNAL</a><br>(https://b.hippoz.xyz/)<br></li>
<li><a href="https://drone.hippoz.xyz/">drone DOWN</a><br>(https://drone.hippoz.xyz/)<br></li>
</ul>
</div>
</body>
</html>

View file

@ -1,29 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="hippoz personal website homepage">
<title>hippoz</title>
<link rel="stylesheet" href="res/style.css">
</head>
<body class="wave-background">
<div class="card layout-card navigation noselect">
<div class="navigation-branding">
<b class="navigation-branding-text">hippoz.</b>
</div>
<div class="navigation-buttons">
<a href="index.html" class="button-default button-selected">home</a>
<a href="blog.html" class="button-default">blog</a>
<a href="https://git.hippoz.xyz" class="button-default">git</a>
<a href="me.html" class="button-default">me</a>
</div>
</div>
<div class="card layout-card">
<h2>hippoz's website</h2>
<p>i think</p>
</div>
</body>
</html>

32
me.html
View file

@ -1,32 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="hippoz">
<title>hippoz</title>
<link rel="stylesheet" href="res/style.css">
</head>
<body class="wave-background">
<div class="card layout-card navigation noselect">
<div class="navigation-branding">
<b class="navigation-branding-text">hippoz.</b>
</div>
<div class="navigation-buttons">
<a href="index.html" class="button-default">home</a>
<a href="blog.html" class="button-default">blog</a>
<a href="https://git.hippoz.xyz" class="button-default">git</a>
<a href="me.html" class="button-default button-selected">me</a>
</div>
</div>
<div class="card layout-card">
<h2>hello</h2>
<p>i think</p>
<br>
<br>
<p>i'm hippoz. that's about it.</p>
</div>
</body>
</html>

View file

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 67 KiB

View file

@ -3,20 +3,20 @@
--accent-bg-color: #d4d3d3; --accent-bg-color: #d4d3d3;
--button-accent-color: #3d3d3d; --button-accent-color: #3d3d3d;
--selected-bg-color: #2E2E2E; --selected-bg-color: #2E2E2E;
--hover-bg-color: #555555; --hover-bg-color: #4d4d4d;
--card-border-radius: 0; --card-border-radius: 8px;
--button-border-radius: 4px; --button-border-radius: 6px;
--main-font-weight: 500; --main-font-weight: 500;
--cards-length: 600px; --cards-length: 600px;
--fonts-proportional: -apple-system,"Segoe UI",system-ui,"Roboto","Helvetica Neue","Arial"; --fonts-regular: "Noto Sans","Liberation Sans",sans-serif;
--fonts-monospace: "SFMono-Regular","Menlo","Monaco","Consolas","Liberation Mono","Courier New",monospace,var(--fonts-emoji);
--fonts-emoji: "Apple Color Emoji","Segoe UI Emoji","Noto Color Emoji","Twemoji Mozilla";
--fonts-regular: var(--fonts-override,var(--fonts-proportional)),"Noto Sans","Liberation Sans",var(--fonts-emoji),sans-serif;
} }
body { body {
background: var(--body-bg-color); background-color: var(--body-bg-color);
background-image: url(wave.svg);
background-size: 100% auto;
background-repeat: no-repeat;
font-family: var(--fonts-regular); font-family: var(--fonts-regular);
font-weight: var(--main-font-weight); font-weight: var(--main-font-weight);
} }
@ -27,19 +27,13 @@ br {
margin-top: 2px; margin-top: 2px;
} }
.wave-background {
background-image: url(wave.svg);
background-size: 100% auto;
background-repeat: no-repeat;
}
.card { .card {
margin: 25px auto; margin: 36px auto;
padding: 20px; padding: 26px;
background: var(--accent-bg-color); background: var(--accent-bg-color);
border-radius: var(--card-border-radius); border-radius: var(--card-border-radius);
box-shadow: 2px -2px 38px 1px rgba(0,0,0,0.59); box-shadow: 0 0 30px 1px rgba(0, 0, 0, 0.45);
} }
.card.layout-card { .card.layout-card {
@ -105,6 +99,6 @@ br {
@media screen and (max-width: 768px) { @media screen and (max-width: 768px) {
.card.layout-card { .card.layout-card {
width: 80%; width: 85%;
} }
} }

View file

Before

Width:  |  Height:  |  Size: 810 B

After

Width:  |  Height:  |  Size: 810 B

View file

@ -1,3 +1,2 @@
User-agent: * User-agent: *
Disallow: /extra/
Disallow: /res/ Disallow: /res/

380
package-lock.json generated Normal file
View file

@ -0,0 +1,380 @@
{
"name": "website-generator",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "website-generator",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"js-beautify": "^1.14.0"
}
},
"node_modules/abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"node_modules/config-chain": {
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
"integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
"dependencies": {
"ini": "^1.3.4",
"proto-list": "~1.2.1"
}
},
"node_modules/editorconfig": {
"version": "0.15.3",
"resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz",
"integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==",
"dependencies": {
"commander": "^2.19.0",
"lru-cache": "^4.1.5",
"semver": "^5.6.0",
"sigmund": "^1.0.1"
},
"bin": {
"editorconfig": "bin/editorconfig"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"node_modules/glob": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/ini": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
},
"node_modules/js-beautify": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.14.0.tgz",
"integrity": "sha512-yuck9KirNSCAwyNJbqW+BxJqJ0NLJ4PwBUzQQACl5O3qHMBXVkXb/rD0ilh/Lat/tn88zSZ+CAHOlk0DsY7GuQ==",
"dependencies": {
"config-chain": "^1.1.12",
"editorconfig": "^0.15.3",
"glob": "^7.1.3",
"nopt": "^5.0.0"
},
"bin": {
"css-beautify": "js/bin/css-beautify.js",
"html-beautify": "js/bin/html-beautify.js",
"js-beautify": "js/bin/js-beautify.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/lru-cache": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
"integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
"dependencies": {
"pseudomap": "^1.0.2",
"yallist": "^2.1.2"
}
},
"node_modules/minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/nopt": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
"integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
"dependencies": {
"abbrev": "1"
},
"bin": {
"nopt": "bin/nopt.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/proto-list": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
"integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk="
},
"node_modules/pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
},
"node_modules/semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"bin": {
"semver": "bin/semver"
}
},
"node_modules/sigmund": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
"integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA="
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"node_modules/yallist": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
}
},
"dependencies": {
"abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
},
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"config-chain": {
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
"integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
"requires": {
"ini": "^1.3.4",
"proto-list": "~1.2.1"
}
},
"editorconfig": {
"version": "0.15.3",
"resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz",
"integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==",
"requires": {
"commander": "^2.19.0",
"lru-cache": "^4.1.5",
"semver": "^5.6.0",
"sigmund": "^1.0.1"
}
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"glob": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"requires": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"ini": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
},
"js-beautify": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.14.0.tgz",
"integrity": "sha512-yuck9KirNSCAwyNJbqW+BxJqJ0NLJ4PwBUzQQACl5O3qHMBXVkXb/rD0ilh/Lat/tn88zSZ+CAHOlk0DsY7GuQ==",
"requires": {
"config-chain": "^1.1.12",
"editorconfig": "^0.15.3",
"glob": "^7.1.3",
"nopt": "^5.0.0"
}
},
"lru-cache": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
"integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
"requires": {
"pseudomap": "^1.0.2",
"yallist": "^2.1.2"
}
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"requires": {
"brace-expansion": "^1.1.7"
}
},
"nopt": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
"integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
"requires": {
"abbrev": "1"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1"
}
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
},
"proto-list": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
"integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk="
},
"pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
},
"sigmund": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
"integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA="
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"yallist": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
}
}
}

18
package.json Normal file
View file

@ -0,0 +1,18 @@
{
"name": "website-generator",
"version": "1.0.0",
"description": "",
"main": "build.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://git.hippoz.xyz/hippoz/homepage"
},
"author": "",
"license": "MIT",
"dependencies": {
"js-beautify": "^1.14.0"
}
}

12
src/blog.page.js Normal file
View file

@ -0,0 +1,12 @@
const Page = require("./components/Page.component");
module.exports = Page({
title: "hippoz blog",
description: "hippoz blog"
})(`
<h2>hippoz's blog</h2>
<p>i think</p>
<br>
<br>
<p>no articles yet</p>
`);

View file

@ -0,0 +1 @@
module.exports = ({ link, text }) => `<a href="${link}" class="button-default">${text}</a>`;

View file

@ -0,0 +1,16 @@
const { navigationLinks, navigationBrandingText } = require("../env");
const LinkButton = require("./LinkButton.component");
module.exports = () => `
<div class="card layout-card noselect">
<div class="navigation-branding">
<b class="navigation-branding-text">${navigationBrandingText}</b>
</div>
<div class="navigation-buttons">
${
navigationLinks.map(
([link, text]) => LinkButton({ link, text })
).join("")
}
</div>
</div>`;

View file

@ -0,0 +1,19 @@
const Navigation = require("./Navigation.component");
module.exports = ({ title="page", description="a page" }) => (content) =>
`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="${description}">
<title>${title}</title>
<link rel="stylesheet" href="res/style.css">
</head>
<body>
${Navigation()}
<div class="card layout-card">
${content}
</div>
</body>
</html>`;

9
src/env/index.js vendored Normal file
View file

@ -0,0 +1,9 @@
module.exports = {
navigationLinks: [
["index.html", "home"],
["blog.html", "blog"],
["me.html", "me"],
["https://git.hippoz.xyz", "git"],
],
navigationBrandingText: "hippoz."
}

9
src/index.page.js Normal file
View file

@ -0,0 +1,9 @@
const Page = require("./components/Page.component");
module.exports = Page({
title: "hippoz",
description: "hippoz website homepage"
})(`
<h2>hippoz's website</h2>
<p>i think</p>
`);

12
src/me.page.js Normal file
View file

@ -0,0 +1,12 @@
const Page = require("./components/Page.component");
module.exports = Page({
title: "about",
description: "hippoz about page"
})(`
<h2>hello</h2>
<p>i think</p>
<br>
<br>
<p>i'm hippoz. that's about it.</p>
`);