Browse Source

Bring over the new development from zeshaw.com.

master
Zed A. Shaw 1 month ago
parent
commit
99fe6b9521
  1. 2
      .eslintrc.json
  2. 24
      README.md
  3. 7
      client/components/Markdown.svelte
  4. 21
      client/components/Sidebar.svelte
  5. 69
      package-lock.json
  6. 9
      package.json
  7. 10
      rendered/Layout.svelte
  8. 49
      rendered/pages/blog.js
  9. 85
      rendered/pages/blog/[slug]/index.svelte
  10. 25
      static/global.css

2
.eslintrc.json

@ -232,7 +232,7 @@
"require-atomic-updates": "warn",
"require-await": "off",
"require-jsdoc": "off",
"require-unicode-regexp": "error",
"require-unicode-regexp": "off",
"rest-spread-spacing": "error",
"semi": "off",
"semi-spacing": "error",

24
README.md

@ -1 +1,25 @@
Install
===
You can install the project with a few commands:
```
git clone --depth 1 git@git.learnjsthehardway.com:zedshaw/ljsthw-project-template.git yourproject
cd yourproject
npm install .
./scripts/init.js
```
You can then either `rm -rf .git` and make your own git, or you can make a branch:
```
git checkout -b yourproject
```
You'll also want to disable push so you don't accidentally keep trying to push to the remote.
```
git config branch.master.pushRemote no_push
```
I'm actually not sure if this is needed but it should be an extra help to keep your from trying.

7
client/components/Markdown.svelte

@ -3,15 +3,16 @@
export let content = "<b>Markdown:</b> No content? Forgot to #await?";
export let with_p = true;
export let using = snarkdown;
const render = () => {
// the documentation for snarkdown says do NOT use sanitize, read that
// as usual there are timing issues so give some default values
if(with_p) {
if(with_p && using === snarkdown) {
// weird that snarkdown doesn't do this, or maybe I just don't see the option
return content.split("\n\n").map(line => `<p>${snarkdown(line)}</p>`).join("\n");
return content.split("\n\n").map(line => `<p>${using(line)}</p>`).join("\n");
} else {
return snarkdown(content);
return using(content);
}
}

21
client/components/Sidebar.svelte

@ -77,11 +77,6 @@
sidebar items .with-icon span.with-icon {
display: none;
}
sidebar :global(svg) {
width: 2em;
height: 2em;
}
}
</style>
@ -95,13 +90,21 @@
{#each menu as item, i}
{#if item.heading}
<h3>{ item.title }</h3>
{:else if item.icon}
<a class="with-icon" class:active={ item.active } on:click={() => activate(i, item)}>
{:else if item.href }
<a class:with-icon={ item.icon } class:without-icon={ !item.icon} class:active={ item.active } href={ item.href }>
{#if item.icon}
<Icon size={ icon_size } name={ item.icon } light={ item.active } /> <span class="with-icon">{ item.title }</span>
{:else}
<span class="without-icon">{ item.title }</span>
{/if}
</a>
{:else}
<a class="without-icon" class:active={ item.active } on:click={() => activate(i, item)}>
<span class="without-icon">{ item.title }</span>
<a class:with-icon={ item.icon } class:without-icon={ !item.icon} class:active={ item.active } on:click={() => activate(i, item)}>
{#if item.icon}
<Icon size={ icon_size } name={ item.icon } light={ item.active } /> <span class="with-icon">{ item.title }</span>
{:else}
<span class="without-icon">{ item.title }</span>
{/if}
</a>
{/if}
{/each}

69
package-lock.json

@ -640,25 +640,25 @@
"integrity": "sha512-IJy1jRL01x7p6UEpgKa1lVLstMUx8EiIR8pPoS5sBfsHEoeLkzYiNpAfxPx8zLDUJyS1yBbChJjcWdPqyH285w=="
},
"@nodelib/fs.scandir": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz",
"integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==",
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
"requires": {
"@nodelib/fs.stat": "2.0.4",
"@nodelib/fs.stat": "2.0.5",
"run-parallel": "^1.1.9"
}
},
"@nodelib/fs.stat": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz",
"integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q=="
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="
},
"@nodelib/fs.walk": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz",
"integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==",
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
"requires": {
"@nodelib/fs.scandir": "2.1.4",
"@nodelib/fs.scandir": "2.1.5",
"fastq": "^1.6.0"
}
},
@ -1459,6 +1459,21 @@
"resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
"integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="
},
"autolinker": {
"version": "3.14.3",
"resolved": "https://registry.npmjs.org/autolinker/-/autolinker-3.14.3.tgz",
"integrity": "sha512-t81i2bCpS+s+5FIhatoww9DmpjhbdiimuU9ATEuLxtZMQ7jLv9fyFn7SWNG8IkEfD4AmYyirL1ss9k1aqVWRvg==",
"requires": {
"tslib": "^1.9.3"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
}
},
"autoprefixer": {
"version": "9.8.6",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz",
@ -4968,16 +4983,15 @@
"dev": true
},
"fast-glob": {
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz",
"integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==",
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz",
"integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==",
"requires": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
"glob-parent": "^5.1.0",
"glob-parent": "^5.1.2",
"merge2": "^1.3.0",
"micromatch": "^4.0.2",
"picomatch": "^2.2.1"
"micromatch": "^4.0.4"
}
},
"fast-json-stable-stringify": {
@ -5007,9 +5021,9 @@
"dev": true
},
"fastq": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz",
"integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==",
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
"integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
"requires": {
"reusify": "^1.0.4"
}
@ -6962,9 +6976,9 @@
"dev": true
},
"marked": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/marked/-/marked-2.1.3.tgz",
"integrity": "sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA=="
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/marked/-/marked-3.0.4.tgz",
"integrity": "sha512-jBo8AOayNaEcvBhNobg6/BLhdsK3NvnKWJg33MAAPbvTWiG4QBn9gpW1+7RssrKu4K1dKlN+0goVQwV41xEfOA=="
},
"matcher": {
"version": "3.0.0",
@ -9529,6 +9543,15 @@
"mdast-util-to-markdown": "^0.6.0"
}
},
"remarkable": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/remarkable/-/remarkable-2.0.1.tgz",
"integrity": "sha512-YJyMcOH5lrR+kZdmB0aJJ4+93bEojRZ1HGDn9Eagu6ibg7aVZhc3OWbbShRid+Q5eAfsEqWxpe+g5W5nYNfNiA==",
"requires": {
"argparse": "^1.0.10",
"autolinker": "^3.11.0"
}
},
"render-media": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/render-media/-/render-media-4.1.0.tgz",

9
package.json

@ -11,7 +11,8 @@
"lib",
"api",
"static",
"services"
"services",
"posts"
],
"extensions": "js,cjs,mjs,svelte"
}
@ -28,7 +29,7 @@
"queue": "nodemon --ignore public services/qserver.js",
"test": "npx ava tests/**/*.js",
"watch": "npm-watch test",
"7ty": "cd rendered && 7ty --watch pages,src,../static",
"7ty": "cd rendered && 7ty --watch pages,client,lib,../static,../posts",
"knex": "knex --knexfile=knexfile.cjs"
},
"devDependencies": {
@ -84,9 +85,10 @@
"express-formidable": "^1.2.0",
"express-session": "^1.17.1",
"faker": "^5.5.3",
"fast-glob": "^3.2.7",
"knex": "^0.95.4",
"knex-paginate": "^2.3.0",
"marked": "^2.1.3",
"marked": "^3.0.4",
"memorystore": "^1.6.6",
"morgan": "^1.10.0",
"node-fetch": "^2.6.1",
@ -101,6 +103,7 @@
"pm2": "^4.5.6",
"randomcolor": "^0.6.2",
"redis": "^3.1.2",
"remarkable": "^2.0.1",
"rollup-plugin-jscc": "^2.0.0",
"sirv-cli": "^1.0.0",
"snarkdown": "^2.0.0",

10
rendered/Layout.svelte

@ -10,11 +10,15 @@
export let fullscreen = false;
export let centered = false;
export let header = true;
export let footer = true;
export let testid;
export let bare = false;
</script>
<Header />
{#if header}
<Header />
{/if}
{#if !bare}
<main class:fullscreen={fullscreen} class:centered={ centered } data-testid={ testid }>
@ -24,4 +28,6 @@
<slot></slot>
{/if}
<Footer />
{#if footer}
<Footer />
{/if}

49
rendered/pages/blog.js

@ -0,0 +1,49 @@
import fs from "fs";
import path from "path";
import glob from "fast-glob";
import assert from "assert";
const POSTS = [];
export const posts = () => POSTS;
export const load = () => {
// just a dumb cache to avoid reading the files over and over
// cache only works if load isn't async!
if(POSTS.length === 0) {
let full_path = path.join(process.cwd(), "..", "/posts");
let posts_regex = `${full_path}/*.md`;
assert(!posts_regex.includes("//"), `You have a // in your path which fast-glob hates: ${posts_regex}`);
const files = glob.sync(posts_regex);
for(let post of files.reverse()) {
try {
const content = fs.readFileSync(post).toString();
const [data, body] = content.split(/\n------\n/);
const metadata = JSON.parse(data);
const slug = path.basename(post, path.extname(post));
metadata.slug = slug; // duplicate it here since it's needed all over
if(!metadata.title) {
// TODO: disgusting, didn't I use something that got this automatically?
const title = body.trim().split("\n")[0];
metadata.title = title.split("#")[1];
}
POSTS.push({slug, metadata, body});
} catch(error) {
console.error(error, `Failure processing post: ${post}`);
}
}
process.POSTS = POSTS;
return POSTS;
} else {
return POSTS;
}
}
export default { load, POSTS };

85
rendered/pages/blog/[slug]/index.svelte

@ -0,0 +1,85 @@
<script context="module">
import blog from "$/rendered/pages/blog.js";
export const getPaths = () => {
return blog.load();
}
export const getData = ({ slug, metadata, body }) => {
return {slug, metadata, body}
}
</script>
<script>
import Layout from '$/rendered/Layout.svelte';
import Markdown from "$/client/components/Markdown.svelte";
import IconImage from "$/client/components/IconImage.svelte";
import { Remarkable } from "remarkable";
import { linkify } from "remarkable/linkify";
export let metadata;
export let body;
export let slug;
let related = blog.POSTS.slice(0, 5).map(p => {
let href = `/blog/${p.slug}`;
let active = slug === p.slug;
return {title: p.metadata.title, href, active, icon: p.metadata.icon};
});
const md = new Remarkable("full", { html: true });
md.use(linkify);
const render = (content) => {
return md.render(content);
}
</script>
<style>
content {
overflow: auto;
padding: 1rem;
display: flex;
}
hero {
width: 100%;
}
hero.icon {
display: block;
}
hero.icon:hover div {
opacity: 0.2;
}
hero cover {
font-size: 2vw;
}
</style>
<Layout bare={ true }>
<hero class="main" class:icon={ !metadata.image } data-testid="home-page">
{#if metadata.image}
<img src={ metadata.image } />
{:else if metadata.icon}
<div>
<IconImage name={ metadata.icon} />
</div>
{/if}
<cover>
<h1>{ metadata.title }</h1>
{#if metadata.summary }
<span id="slogan">{ metadata.summary }</span>
{/if}
<br/>
</cover>
</hero>
<content>
<Markdown content={ body } using={ render }/>
</content>
</Layout>

25
static/global.css

@ -345,6 +345,7 @@ img {
* from the 1990s when HTML was all about text and images were considered text.
*/
display: block;
width: 100%;
}
figure {
@ -462,7 +463,6 @@ table {
max-width: 100%;
overflow-x: auto;
padding: 0;
white-space: nowrap;
}
table td,
@ -529,10 +529,6 @@ blockquote footer {
max-width: 100% !important;
}
content {
display: none !important;
}
phone-warn {
display: flex !important;
}
@ -1155,10 +1151,6 @@ hero figure {
padding: 0;
}
hero.main:hover figure {
transform: scale(1.03);
filter: blur(4px);
}
hero cover {
display: flex;
@ -1174,8 +1166,8 @@ hero cover {
font-size: 4vw;
color: var(--color-overlay-text);
background: var(--color-overlay-background);
text-shadow: 8px 8px 5px var(--color-overlay-shadow);
transition: background 0.5s ease-out;
opacity: 0;
}
hero cover h1 {
@ -1198,6 +1190,19 @@ hero cover a i {
text-shadow: none;
}
hero.main:hover figure {
transform: scale(1.03);
filter: blur(4px);
}
hero.main:hover img {
opacity: 0.2;
}
hero.main:hover cover {
opacity: 1;
}
hero.middle {
background-color: var(--color-bg-tertiary);
padding-top: 5rem;

Loading…
Cancel
Save