Browse Source

More refinement of the exercise structure and how it's generated.

20200324-model-modules
Zed A. Shaw 1 month ago
parent
commit
09a7446af5
13 changed files with 136 additions and 67 deletions
  1. +6
    -5
      db/modules/html_basics/exercises/01-intro.md
  2. +14
    -0
      ecosystem.config.js
  3. +16
    -4
      generator.js
  4. +57
    -42
      lib/docs.js
  5. +27
    -0
      package-lock.json
  6. +2
    -1
      package.json
  7. +6
    -5
      protected/media/modules/html_basics/exercises/01-intro.html
  8. +0
    -4
      src/routes/auth/index.svelte
  9. +4
    -2
      src/routes/modules/[slug]/[exercise].svelte
  10. +1
    -1
      static/api/modules/html_basics/exercises/01-intro.json
  11. +1
    -1
      static/api/modules/html_basics/index.json
  12. +1
    -1
      static/feed.atom
  13. +1
    -1
      static/feed.rss

+ 6
- 5
db/modules/html_basics/exercises/01-intro.md View File

@@ -127,11 +127,12 @@ That is a lot of information to start, but you should be able to take the follow

```html
<html>
<head>
<title>My First Web Page</title>
<body>
<p>This word right <em>here</em>
</body>
<head>
<title>My First Web Page</title>
</head>
<body>
<p>This word right <em>here</em>
</body>
</html>
```


+ 14
- 0
ecosystem.config.js View File

@@ -14,6 +14,20 @@ module.exports = {
NODE_ENV: 'production'
}
},
{
name: 'generator',
script: 'generator.js',
instances: 1,
autorestart: true,
watch: false,
max_memory_restart: '1G',
env: {
NODE_ENV: 'development'
},
env_production: {
NODE_ENV: 'production'
}
},
{
name: 'gulp',
script: 'gulp',

+ 16
- 4
generator.js View File

@@ -10,6 +10,7 @@ const cleaner = require('deep-cleaner');
const glob = require('glob').sync;
const path = require('path');
const Feed = require('feed').Feed;
const watch = require('watch');


const facts = {
@@ -32,7 +33,6 @@ const fix_module_base = (t) => {
// is really only done once in here. Basically we have to strip /exercises/ from the url
// to form the base for header links to work because Svelte requires full paths.
let parts = t.tail_no_ext.split(path.sep);
log.debug("PARTS", parts);
return `/modules/${parts[0]}/${parts[2]}`;
}

@@ -184,10 +184,22 @@ const need_video_data = build.rule('video info written', {
}
});


(async () => build.run(facts, [
const targets = [
need_video_stills, need_video_thumbs, need_video_data,
need_api_update, needs_blog, needs_modules,
need_blog_index, need_modules_index
]))();
];

let run_build = () => {
log.info("Changes detected, running build.");
(async () => build.run(facts, targets))();
}

run_build();

watch.createMonitor('./db', {ignoreDotFiles: true}, (mon) => {
mon.on('created', run_build);
mon.on('changed', run_build);
mon.on('removed', run_build);
});


+ 57
- 42
lib/docs.js View File

@@ -6,58 +6,73 @@ const { log } = require('./logging');
const fs = require('fs');

function highlight(str, lang) {
// TODO: Prism mentions if you try to include languages here then all of them get included
// TODO: Prism recommends loading languages in the rollup stage
if(lang in Prism.languages) {
return Prism.highlight(str, Prism.languages[lang], lang);
} else {
console.log(`!!!!!!!!!!!!!!! Language ${lang} not found in Prism.`);
return '';
}
// TODO: Prism mentions if you try to include languages here then all of them get included
// TODO: Prism recommends loading languages in the rollup stage
if(lang in Prism.languages) {
return Prism.highlight(str, Prism.languages[lang], lang);
} else {
console.log(`!!!!!!!!!!!!!!! Language ${lang} not found in Prism.`);
return '';
}
}

/* A separate renderer just for the titles that doesn't need anything else. */
const title_render = new Remarkable('full').use(rem => {
let pass = (tokens, idx) => '';
rem.renderer.rules.paragraph_open = pass;
rem.renderer.rules.paragraph_close = pass;
});

function split(raw_md) {
let [metadata, ...body] = raw_md.split('------');
metadata = JSON.parse(metadata);
body = body.join('------');
return [metadata, body];
let [metadata, ...body] = raw_md.split('------');
metadata = JSON.parse(metadata);
body = body.join('------');
return [metadata, body];
}

function render(raw_md, base_url) {
let toc = [];
let [metadata, body] = split(raw_md);

const renderer = new Remarkable('full', {
highlight
}).use(linkify).use(rem => {
rem.renderer.rules.heading_open = (tokens, idx) => {
let level = tokens[idx].hLevel;
let content = tokens[idx + 1].content;
let slug = urlSlug(content);
toc.push({level, content, slug});
return `<a href="${base_url}#${slug}"><h${level} id="${slug}">`;
}

rem.renderer.rules.heading_close = (tokens, idx) => {
return `</h${tokens[idx].hLevel}></a>\n`;
}
});

let content = renderer.render(body);

// now we can use the TOC to figure out a title
metadata.title = toc[0].content;
return {toc, content, metadata};
let toc = [];
let [metadata, body] = split(raw_md);

const renderer = new Remarkable('full', {
highlight
}).use(linkify).use(rem => {
rem.renderer.rules.heading_open = (tokens, idx) => {
let level = tokens[idx].hLevel;
let content = tokens[idx + 1].content;
let slug = urlSlug(content);
toc.push({level, content, slug});
return `<a href="${base_url}#${slug}"><h${level} id="${slug}">`;
}

rem.renderer.rules.heading_close = (tokens, idx) => {
return `</h${tokens[idx].hLevel}></a>\n`;
}
});

let content = renderer.render(body);

// now we can use the TOC to figure out a title
metadata.title = toc[0].content;

try {
// finally, run the renderer on all of the TOC
toc.forEach(t => t.html = title_render.render(t.content));
} catch(error) {
log.error(error);
}

return {toc, content, metadata};
}

function load(path, base_url) {
log.debug("Rendering ", path);
let raw_md = fs.readFileSync(path);
return render(raw_md.toString(), base_url);
log.debug("Rendering ", path);
let raw_md = fs.readFileSync(path);
return render(raw_md.toString(), base_url);
}

module.exports = {
render,
load,
split
render,
load,
split
}

+ 27
- 0
package-lock.json View File

@@ -9964,6 +9964,12 @@
"trim-newlines": "^1.0.0"
}
},
"merge": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz",
"integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==",
"dev": true
},
"merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -15289,6 +15295,27 @@
"makeerror": "1.0.x"
}
},
"watch": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/watch/-/watch-1.0.2.tgz",
"integrity": "sha1-NApxe952Vyb6CqB9ch4BR6VR3ww=",
"dev": true,
"requires": {
"exec-sh": "^0.2.0",
"minimist": "^1.2.0"
},
"dependencies": {
"exec-sh": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.2.tgz",
"integrity": "sha512-FIUCJz1RbuS0FKTdaAafAByGS0CPvU3R0MeHxgtl+djzCc//F8HakL8GzmVNZanasTbTAY/3DRFA0KpVqj/eAw==",
"dev": true,
"requires": {
"merge": "^1.2.0"
}
}
}
},
"webidl-conversions": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",

+ 2
- 1
package.json View File

@@ -80,6 +80,7 @@
"sapper": "^0.27.10",
"sharp": "^0.25.2",
"svelte": "^3.20.1",
"svelte-preprocess": "^3.5.0"
"svelte-preprocess": "^3.5.0",
"watch": "^1.0.2"
}
}

+ 6
- 5
protected/media/modules/html_basics/exercises/01-intro.html View File

@@ -85,11 +85,12 @@
<a href="/modules/html_basics/01-intro#a-complete-html-page"><h2 id="a-complete-html-page">A Complete HTML Page</h2></a>
<p>That is a lot of information to start, but you should be able to take the following code and make some educated guesses as to what it does:</p>
<pre><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>html</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>title</span><span class="token punctuation">></span></span>My First Web Page<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>title</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>This word right <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>em</span><span class="token punctuation">></span></span>here<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>em</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>title</span><span class="token punctuation">></span></span>My First Web Page<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>title</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>This word right <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>em</span><span class="token punctuation">></span></span>here<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>em</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span>
</code></pre>
<p>Type this code into your <code>index.html</code> file and load it in the browser again. You can usually just refresh the page and your new content will show up. If you typed it correctly then it should look the exact same as before, but now you should see a title in the browser window bar.</p>

+ 0
- 4
src/routes/auth/index.svelte View File

@@ -28,10 +28,6 @@

if(response.user) {
email_sent = true;
// warn them before closing
window.onbeforeunload = function(e) {
e.returnValue='If you leave this page now you will have to request a new login token.';
};
} else {
error = response.message;
email_sent = false;

+ 4
- 2
src/routes/modules/[slug]/[exercise].svelte View File

@@ -46,18 +46,20 @@
</a>
</div>
{#each toc as item}
<div class="divider text-center"></div>
<li class="nav-item">
<a href="/modules/{module}/{exercise}/#{item.slug}">{item.content}</a>
<a href="/modules/{module}/{exercise}/#{item.slug}">{@html item.html}</a>
</li>
{/each}
<div class="divider text-center"></div>

{#if metadata.video }
<div class="divider text-center"></div>
<div class="nav-item">
<a href="/modules/{module}/{exercise}/#video">Video</a>
</div>
{/if}

<div class="divider text-center"></div>
<div class="nav-item">
<a href="/modules/{module}/{exercise}/#questions">Questions</a>
</div>

+ 1
- 1
static/api/modules/html_basics/exercises/01-intro.json View File

@@ -1 +1 @@
{"toc":[{"level":1,"content":"HTML Basics Part 1","slug":"html-basics-part-1"},{"level":2,"content":"What _Is_ HTML?","slug":"what-is-html"},{"level":2,"content":"What Does HTML _Code_ Look Like?","slug":"what-does-html-code-look-like"},{"level":3,"content":"Breaking It Down","slug":"breaking-it-down"},{"level":2,"content":"Programmers _Love_ Nesting","slug":"programmers-love-nesting"},{"level":2,"content":"A Complete HTML Page","slug":"a-complete-html-page"},{"level":2,"content":"Video Contents","slug":"video-contents"}],"metadata":{"author":"Zed A. Shaw","date":"Mar 25, 2020","module":"/modules/html_basics","id":2,"title":"HTML Basics Part 1","completed":false,"image":"/images/hero-holder.svg","summary":"This will first introduce some basic terminology for HTML.","video":{"src":"/images/sample.mp4","poster":"/images/sample.jpg","preload":"none"},"slug":"01-intro"}}
{"toc":[{"level":1,"content":"HTML Basics Part 1","slug":"html-basics-part-1","html":"HTML Basics Part 1"},{"level":2,"content":"What _Is_ HTML?","slug":"what-is-html","html":"What <em>Is</em> HTML?"},{"level":2,"content":"What Does HTML _Code_ Look Like?","slug":"what-does-html-code-look-like","html":"What Does HTML <em>Code</em> Look Like?"},{"level":3,"content":"Breaking It Down","slug":"breaking-it-down","html":"Breaking It Down"},{"level":2,"content":"Programmers _Love_ Nesting","slug":"programmers-love-nesting","html":"Programmers <em>Love</em> Nesting"},{"level":2,"content":"A Complete HTML Page","slug":"a-complete-html-page","html":"A Complete HTML Page"},{"level":2,"content":"Video Contents","slug":"video-contents","html":"Video Contents"}],"metadata":{"author":"Zed A. Shaw","date":"Mar 25, 2020","module":"/modules/html_basics","id":2,"title":"HTML Basics Part 1","completed":false,"image":"/images/hero-holder.svg","summary":"This will first introduce some basic terminology for HTML.","video":{"src":"/images/sample.mp4","poster":"/images/sample.jpg","preload":"none"},"slug":"01-intro"}}

+ 1
- 1
static/api/modules/html_basics/index.json View File

@@ -1 +1 @@
{"author":"Zed A. Shaw","date":"Mar 25, 2020","url":"/modules/html_basics","slug":"html_basics","title":"HTML Basics","tag":"what","subtitle":"Start here if you know nothing.","summary":"An introduction to basic HTML for the web.","exercises":[{"toc":[{"level":1,"content":"HTML Basics Part 1","slug":"html-basics-part-1"},{"level":2,"content":"What _Is_ HTML?","slug":"what-is-html"},{"level":2,"content":"What Does HTML _Code_ Look Like?","slug":"what-does-html-code-look-like"},{"level":3,"content":"Breaking It Down","slug":"breaking-it-down"},{"level":2,"content":"Programmers _Love_ Nesting","slug":"programmers-love-nesting"},{"level":2,"content":"A Complete HTML Page","slug":"a-complete-html-page"},{"level":2,"content":"Video Contents","slug":"video-contents"}],"metadata":{"author":"Zed A. Shaw","date":"Mar 25, 2020","module":"/modules/html_basics","id":2,"title":"HTML Basics Part 1","completed":false,"image":"/images/hero-holder.svg","summary":"This will first introduce some basic terminology for HTML.","video":{"src":"/images/sample.mp4","poster":"/images/sample.jpg","preload":"none"},"slug":"01-intro"}}]}
{"author":"Zed A. Shaw","date":"Mar 25, 2020","url":"/modules/html_basics","slug":"html_basics","title":"HTML Basics","tag":"what","subtitle":"Start here if you know nothing.","summary":"An introduction to basic HTML for the web.","exercises":[{"toc":[{"level":1,"content":"HTML Basics Part 1","slug":"html-basics-part-1","html":"HTML Basics Part 1"},{"level":2,"content":"What _Is_ HTML?","slug":"what-is-html","html":"What <em>Is</em> HTML?"},{"level":2,"content":"What Does HTML _Code_ Look Like?","slug":"what-does-html-code-look-like","html":"What Does HTML <em>Code</em> Look Like?"},{"level":3,"content":"Breaking It Down","slug":"breaking-it-down","html":"Breaking It Down"},{"level":2,"content":"Programmers _Love_ Nesting","slug":"programmers-love-nesting","html":"Programmers <em>Love</em> Nesting"},{"level":2,"content":"A Complete HTML Page","slug":"a-complete-html-page","html":"A Complete HTML Page"},{"level":2,"content":"Video Contents","slug":"video-contents","html":"Video Contents"}],"metadata":{"author":"Zed A. Shaw","date":"Mar 25, 2020","module":"/modules/html_basics","id":2,"title":"HTML Basics Part 1","completed":false,"image":"/images/hero-holder.svg","summary":"This will first introduce some basic terminology for HTML.","video":{"src":"/images/sample.mp4","poster":"/images/sample.jpg","preload":"none"},"slug":"01-intro"}}]}

+ 1
- 1
static/feed.atom View File

@@ -2,7 +2,7 @@
<feed xmlns="http://www.w3.org/2005/Atom">
<id>https://learnjsthehardway.com/</id>
<title>Learn JavaScript the Hard Way</title>
<updated>2020-04-12T01:42:07.596Z</updated>
<updated>2020-04-12T18:15:28.398Z</updated>
<generator>LJSTHW Custom Generator</generator>
<author>
<name>Zed A. Shaw</name>

+ 1
- 1
static/feed.rss View File

@@ -4,7 +4,7 @@
<title>Learn JavaScript the Hard Way</title>
<link>https://learnjsthehardway.com/</link>
<description>The blog for the Learn JavaScript the Hard Way course.</description>
<lastBuildDate>Sun, 12 Apr 2020 01:42:07 GMT</lastBuildDate>
<lastBuildDate>Sun, 12 Apr 2020 18:15:28 GMT</lastBuildDate>
<docs>https://validator.w3.org/feed/docs/rss2.html</docs>
<generator>LJSTHW Custom Generator</generator>
<language>en</language>

Loading…
Cancel
Save