From 2e60126ded39c5a56209d4b157c6e65f1290c791 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Fri, 4 Jan 2019 13:14:53 -0500 Subject: [PATCH] Initial commit --- .gitignore | 4 + .vscode/launch.json | 18 + .vscode/settings.json | 9 + lib/generate/categories.ts | 24 + lib/generate/css.ts | 33 + lib/generate/homepage.ts | 6 + lib/generate/index.ts | 19 + lib/generate/missing.ts | 18 + lib/generate/paginated.ts | 49 + lib/generate/posts.ts | 47 + lib/generate/redirects.ts | 32 + lib/generate/rss.ts | 38 + lib/generate/tutorials.ts | 118 ++ lib/index.ts | 16 + lib/layout.ts | 22 + lib/markdown.ts | 11 + lib/metadata.ts | 35 + lib/util.ts | 35 + package-lock.json | 1615 +++++++++++++++++ package.json | 24 + site/404.html.ejs | 14 + site/category.html.ejs | 48 + site/css/dark.scss | 25 + site/css/light.scss | 25 + site/css/main.scss | 357 ++++ site/css/normalize.css | 341 ++++ site/css/syntax-highlighting.css | 81 + site/feed.xml.ejs | 32 + site/includes/article-meta.html.ejs | 16 + site/index.html.ejs | 44 + site/layouts/article.html.ejs | 14 + site/layouts/default.html.ejs | 83 + site/posts/2016-05-05-hello-world.md | 7 + site/posts/2016-05-21-194-porting-spree.md | 44 + site/posts/2016-06-29-introducing-rtfm.md | 12 + site/posts/2016-06-30-forge-1102-tutorials.md | 8 + site/posts/2016-07-28-introducing-mirror.md | 149 ++ site/posts/2016-08-06-kotlin-and-forge.md | 38 + site/posts/2016-08-07-the-great-redesign.md | 29 + site/posts/2016-10-02-type.md | 112 ++ ...2017-02-13-the-pretty-good-minor-update.md | 19 + .../2018-04-23-comments-powered-by-github.md | 37 + site/posts/2019-01-02-reincarnation.md | 44 + site/redirect.html.ejs | 16 + site/tutorial-series.html.ejs | 21 + site/tutorials.html.ejs | 22 + .../advanced-creative-tabs.md | 62 + site/tutorials/forge-modding-1102/armor.md | 206 +++ .../forge-modding-1102/basic-blocks.md | 152 ++ .../basic-forge-blockstates.md | 37 + .../forge-modding-1102/basic-items.md | 134 ++ .../crafting-smelting-recipes.md | 87 + .../forge-modding-1102/creative-tabs.md | 94 + site/tutorials/forge-modding-1102/crops.md | 259 +++ .../dynamic-tile-entity-rendering.md | 439 +++++ site/tutorials/forge-modding-1102/food.md | 76 + .../forge-modding-1102/json-block-models.md | 206 +++ .../forge-modding-1102/json-item-models.md | 36 + .../forge-modding-1102/localization.md | 30 + .../forge-modding-1102/main-mod-class.md | 50 + .../forge-modding-1102/ore-dictionary.md | 188 ++ site/tutorials/forge-modding-1102/overview.md | 60 + .../forge-modding-1102/proxy-system.md | 19 + .../tile-entities-inventory-gui.md | 326 ++++ .../tile-entities-inventory.md | 237 +++ .../forge-modding-1102/tile-entities.md | 266 +++ site/tutorials/forge-modding-1102/tools.md | 412 +++++ .../forge-modding-1102/workspace-setup.md | 50 + .../world-generation-ore.md | 146 ++ .../advanced-creative-tabs.md | 62 + site/tutorials/forge-modding-1112/armor.md | 206 +++ .../forge-modding-1112/basic-blocks.md | 152 ++ .../basic-forge-blockstates.md | 37 + .../forge-modding-1112/basic-items.md | 134 ++ .../crafting-smelting-recipes.md | 87 + .../forge-modding-1112/creative-tabs.md | 94 + site/tutorials/forge-modding-1112/crops.md | 259 +++ .../dynamic-tile-entity-rendering.md | 439 +++++ site/tutorials/forge-modding-1112/food.md | 76 + .../forge-modding-1112/json-block-models.md | 206 +++ .../forge-modding-1112/json-item-models.md | 36 + .../forge-modding-1112/localization.md | 29 + .../forge-modding-1112/main-mod-class.md | 50 + .../forge-modding-1112/ore-dictionary.md | 188 ++ site/tutorials/forge-modding-1112/overview.md | 61 + .../forge-modding-1112/proxy-system.md | 20 + .../tile-entities-inventory-gui.md | 327 ++++ .../tile-entities-inventory.md | 239 +++ .../forge-modding-1112/tile-entities.md | 266 +++ site/tutorials/forge-modding-1112/tools.md | 412 +++++ .../forge-modding-1112/updating-to-1112.md | 32 + .../forge-modding-1112/workspace-setup.md | 50 + .../world-generation-ore.md | 146 ++ .../advanced-creative-tabs.md | 62 + site/tutorials/forge-modding-112/armor.md | 238 +++ .../forge-modding-112/basic-blocks.md | 192 ++ .../basic-forge-blockstates.md | 37 + .../forge-modding-112/basic-items.md | 189 ++ .../forge-modding-112/crafting-recipes.md | 78 + .../forge-modding-112/creative-tabs.md | 94 + site/tutorials/forge-modding-112/crops.md | 230 +++ .../dynamic-tile-entity-rendering.md | 439 +++++ site/tutorials/forge-modding-112/food.md | 69 + .../forge-modding-112/json-block-models.md | 203 +++ .../forge-modding-112/json-item-models.md | 36 + .../forge-modding-112/localization.md | 29 + .../forge-modding-112/main-mod-class.md | 51 + .../forge-modding-112/ore-dictionary.md | 142 ++ site/tutorials/forge-modding-112/overview.md | 58 + .../forge-modding-112/proxy-system.md | 19 + .../forge-modding-112/smelting-recipes.md | 50 + .../tile-entities-inventory-gui.md | 322 ++++ .../tile-entities-inventory.md | 255 +++ .../forge-modding-112/tile-entities.md | 259 +++ site/tutorials/forge-modding-112/tools.md | 432 +++++ .../forge-modding-112/workspace-setup.md | 38 + .../forge-modding-112/world-generation-ore.md | 146 ++ tsconfig.json | 63 + 118 files changed, 14722 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 lib/generate/categories.ts create mode 100644 lib/generate/css.ts create mode 100644 lib/generate/homepage.ts create mode 100644 lib/generate/index.ts create mode 100644 lib/generate/missing.ts create mode 100644 lib/generate/paginated.ts create mode 100644 lib/generate/posts.ts create mode 100644 lib/generate/redirects.ts create mode 100644 lib/generate/rss.ts create mode 100644 lib/generate/tutorials.ts create mode 100644 lib/index.ts create mode 100644 lib/layout.ts create mode 100644 lib/markdown.ts create mode 100644 lib/metadata.ts create mode 100644 lib/util.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 site/404.html.ejs create mode 100644 site/category.html.ejs create mode 100644 site/css/dark.scss create mode 100644 site/css/light.scss create mode 100644 site/css/main.scss create mode 100644 site/css/normalize.css create mode 100644 site/css/syntax-highlighting.css create mode 100644 site/feed.xml.ejs create mode 100644 site/includes/article-meta.html.ejs create mode 100644 site/index.html.ejs create mode 100644 site/layouts/article.html.ejs create mode 100644 site/layouts/default.html.ejs create mode 100644 site/posts/2016-05-05-hello-world.md create mode 100644 site/posts/2016-05-21-194-porting-spree.md create mode 100644 site/posts/2016-06-29-introducing-rtfm.md create mode 100644 site/posts/2016-06-30-forge-1102-tutorials.md create mode 100644 site/posts/2016-07-28-introducing-mirror.md create mode 100644 site/posts/2016-08-06-kotlin-and-forge.md create mode 100644 site/posts/2016-08-07-the-great-redesign.md create mode 100644 site/posts/2016-10-02-type.md create mode 100644 site/posts/2017-02-13-the-pretty-good-minor-update.md create mode 100644 site/posts/2018-04-23-comments-powered-by-github.md create mode 100644 site/posts/2019-01-02-reincarnation.md create mode 100644 site/redirect.html.ejs create mode 100644 site/tutorial-series.html.ejs create mode 100644 site/tutorials.html.ejs create mode 100644 site/tutorials/forge-modding-1102/advanced-creative-tabs.md create mode 100644 site/tutorials/forge-modding-1102/armor.md create mode 100644 site/tutorials/forge-modding-1102/basic-blocks.md create mode 100644 site/tutorials/forge-modding-1102/basic-forge-blockstates.md create mode 100644 site/tutorials/forge-modding-1102/basic-items.md create mode 100644 site/tutorials/forge-modding-1102/crafting-smelting-recipes.md create mode 100644 site/tutorials/forge-modding-1102/creative-tabs.md create mode 100644 site/tutorials/forge-modding-1102/crops.md create mode 100644 site/tutorials/forge-modding-1102/dynamic-tile-entity-rendering.md create mode 100644 site/tutorials/forge-modding-1102/food.md create mode 100644 site/tutorials/forge-modding-1102/json-block-models.md create mode 100644 site/tutorials/forge-modding-1102/json-item-models.md create mode 100644 site/tutorials/forge-modding-1102/localization.md create mode 100644 site/tutorials/forge-modding-1102/main-mod-class.md create mode 100644 site/tutorials/forge-modding-1102/ore-dictionary.md create mode 100644 site/tutorials/forge-modding-1102/overview.md create mode 100644 site/tutorials/forge-modding-1102/proxy-system.md create mode 100644 site/tutorials/forge-modding-1102/tile-entities-inventory-gui.md create mode 100644 site/tutorials/forge-modding-1102/tile-entities-inventory.md create mode 100644 site/tutorials/forge-modding-1102/tile-entities.md create mode 100644 site/tutorials/forge-modding-1102/tools.md create mode 100644 site/tutorials/forge-modding-1102/workspace-setup.md create mode 100644 site/tutorials/forge-modding-1102/world-generation-ore.md create mode 100644 site/tutorials/forge-modding-1112/advanced-creative-tabs.md create mode 100644 site/tutorials/forge-modding-1112/armor.md create mode 100644 site/tutorials/forge-modding-1112/basic-blocks.md create mode 100644 site/tutorials/forge-modding-1112/basic-forge-blockstates.md create mode 100644 site/tutorials/forge-modding-1112/basic-items.md create mode 100644 site/tutorials/forge-modding-1112/crafting-smelting-recipes.md create mode 100644 site/tutorials/forge-modding-1112/creative-tabs.md create mode 100644 site/tutorials/forge-modding-1112/crops.md create mode 100644 site/tutorials/forge-modding-1112/dynamic-tile-entity-rendering.md create mode 100644 site/tutorials/forge-modding-1112/food.md create mode 100644 site/tutorials/forge-modding-1112/json-block-models.md create mode 100644 site/tutorials/forge-modding-1112/json-item-models.md create mode 100644 site/tutorials/forge-modding-1112/localization.md create mode 100644 site/tutorials/forge-modding-1112/main-mod-class.md create mode 100644 site/tutorials/forge-modding-1112/ore-dictionary.md create mode 100644 site/tutorials/forge-modding-1112/overview.md create mode 100644 site/tutorials/forge-modding-1112/proxy-system.md create mode 100644 site/tutorials/forge-modding-1112/tile-entities-inventory-gui.md create mode 100644 site/tutorials/forge-modding-1112/tile-entities-inventory.md create mode 100644 site/tutorials/forge-modding-1112/tile-entities.md create mode 100644 site/tutorials/forge-modding-1112/tools.md create mode 100644 site/tutorials/forge-modding-1112/updating-to-1112.md create mode 100644 site/tutorials/forge-modding-1112/workspace-setup.md create mode 100644 site/tutorials/forge-modding-1112/world-generation-ore.md create mode 100644 site/tutorials/forge-modding-112/advanced-creative-tabs.md create mode 100644 site/tutorials/forge-modding-112/armor.md create mode 100644 site/tutorials/forge-modding-112/basic-blocks.md create mode 100644 site/tutorials/forge-modding-112/basic-forge-blockstates.md create mode 100644 site/tutorials/forge-modding-112/basic-items.md create mode 100644 site/tutorials/forge-modding-112/crafting-recipes.md create mode 100644 site/tutorials/forge-modding-112/creative-tabs.md create mode 100644 site/tutorials/forge-modding-112/crops.md create mode 100644 site/tutorials/forge-modding-112/dynamic-tile-entity-rendering.md create mode 100644 site/tutorials/forge-modding-112/food.md create mode 100644 site/tutorials/forge-modding-112/json-block-models.md create mode 100644 site/tutorials/forge-modding-112/json-item-models.md create mode 100644 site/tutorials/forge-modding-112/localization.md create mode 100644 site/tutorials/forge-modding-112/main-mod-class.md create mode 100644 site/tutorials/forge-modding-112/ore-dictionary.md create mode 100644 site/tutorials/forge-modding-112/overview.md create mode 100644 site/tutorials/forge-modding-112/proxy-system.md create mode 100644 site/tutorials/forge-modding-112/smelting-recipes.md create mode 100644 site/tutorials/forge-modding-112/tile-entities-inventory-gui.md create mode 100644 site/tutorials/forge-modding-112/tile-entities-inventory.md create mode 100644 site/tutorials/forge-modding-112/tile-entities.md create mode 100644 site/tutorials/forge-modding-112/tools.md create mode 100644 site/tutorials/forge-modding-112/workspace-setup.md create mode 100644 site/tutorials/forge-modding-112/world-generation-ore.md create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d687fe3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.DS_Store +node_modules/ +built/ +out/ diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..acf131f --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,18 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Generate", + "program": "${workspaceFolder}/lib/index.ts", + "preLaunchTask": "tsc: build - tsconfig.json", + "outFiles": [ + "${workspaceFolder}/built/*.js" + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..33d7318 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "search.exclude": { + "**/node_modules": true, + "out": true, + "built": true, + }, + "editor.tabSize": 4, + "editor.insertSpaces": false +} \ No newline at end of file diff --git a/lib/generate/categories.ts b/lib/generate/categories.ts new file mode 100644 index 0000000..bbae10b --- /dev/null +++ b/lib/generate/categories.ts @@ -0,0 +1,24 @@ +import { Page, PostMetadata } from "../metadata"; +import generatePaginated from "./paginated"; + +export default async function homepage(posts: Page[]): Promise> { + const categories = new Map(); + + for (const post of posts) { + const category = (post.metadata).category; + if (!categories.has(category)) { + categories.set(category, []); + } + categories.get(category)!.push(post); + } + + categories.forEach(async (categoryPosts, category) => { + await generatePaginated(categoryPosts, `/${category}/`, "site/category.html.ejs", { + category + }, { + title: `${category} posts` + }); + }); + + return categories; +} \ No newline at end of file diff --git a/lib/generate/css.ts b/lib/generate/css.ts new file mode 100644 index 0000000..6ba7ed7 --- /dev/null +++ b/lib/generate/css.ts @@ -0,0 +1,33 @@ +import { promises as fs } from "fs"; +import * as util from "../util"; +import sass, { Result as SassResult } from "node-sass"; +import ejs from "ejs"; + +function renderSass(data: string, minify = false): Promise { + return new Promise((resolve, reject) => { + sass.render({ + data: data, + outputStyle: minify ? "compressed" : "expanded" + }, (error, result) => { + if (error) reject(error); + else resolve(result); + }); + }); +} + +async function generate(theme: string) { + const filename = `site/css/${theme}.scss`; + + let sass = (await fs.readFile(filename)).toString(); + sass = ejs.render(sass, {}, { + filename: filename + }); + const result = await renderSass(sass); + + util.write(`css/${theme}.css`, result.css); +} + +export default async function css() { + await generate("light"); + await generate("dark"); +} \ No newline at end of file diff --git a/lib/generate/homepage.ts b/lib/generate/homepage.ts new file mode 100644 index 0000000..e3c63c9 --- /dev/null +++ b/lib/generate/homepage.ts @@ -0,0 +1,6 @@ +import { Page } from "../metadata"; +import generatePaginated from "./paginated"; + +export default async function homepage(posts: Page[]) { + await generatePaginated(posts, "/", "site/index.html.ejs"); +} \ No newline at end of file diff --git a/lib/generate/index.ts b/lib/generate/index.ts new file mode 100644 index 0000000..c10ba02 --- /dev/null +++ b/lib/generate/index.ts @@ -0,0 +1,19 @@ +import categories from "./categories"; +import css from "./css"; +import homepage from "./homepage"; +import missing from "./missing"; +import posts from "./posts"; +import redirects from "./redirects"; +import rss from "./rss"; +import tutorials from "./tutorials"; + +export = { + categories, + css, + homepage, + missing, + posts, + redirects, + rss, + tutorials +}; \ No newline at end of file diff --git a/lib/generate/missing.ts b/lib/generate/missing.ts new file mode 100644 index 0000000..36b7db2 --- /dev/null +++ b/lib/generate/missing.ts @@ -0,0 +1,18 @@ +import ejs from "ejs"; +import * as metadata from "../metadata"; +import layout from "../layout"; +import * as util from "../util"; + +export default async function missing() { + const page = await metadata.get("site/404.html.ejs"); + + page.text = ejs.render(page.text, { + metadata: page.metadata + }, { + filename: "site/404.html.ejs" + }); + + page.text = await layout(page.text, page.metadata, page.metadata.layout!); + + util.write("404.html", page.text); +} \ No newline at end of file diff --git a/lib/generate/paginated.ts b/lib/generate/paginated.ts new file mode 100644 index 0000000..373347b --- /dev/null +++ b/lib/generate/paginated.ts @@ -0,0 +1,49 @@ +import path from "path"; +import ejs from "ejs"; +import formatDate from "date-fns/format"; +import * as util from "../util"; +import * as metadata from "../metadata"; +import { Page, PostMetadata } from "../metadata"; +import layout from "../layout"; + +export default async function generatePaginted(posts: Page[], basePath: string, templatePath: string, extraData?: object, extraMetadata?: object) { + const page = await metadata.get(templatePath); + + if (extraMetadata) page.metadata = {...page.metadata, ...extraMetadata}; + + posts = posts.sort((a, b) => { + const aDate = (a.metadata).date; + const bDate = (b.metadata).date; + return bDate.getTime() - aDate.getTime(); + }); + + const chunks = util.chunk(posts, 5); + for (const {chunk, index} of chunks) { + const pageNum = index + 1; + + let data = { + metadata: page.metadata, + posts: chunk, + pagination: { + current: pageNum, + total: chunks.length, + prevLink: pageNum == 1 ? "" : pageNum == 2 ? basePath : path.join(basePath, (pageNum - 1).toString()), + nextLink: pageNum == chunks.length ? "" : path.join(basePath, (pageNum + 1).toString()) + }, + formatDate + }; + if (extraData) data = {...data, ...extraData}; + + let renderedTemplate = ejs.render(page.text, data, { + filename: templatePath + }); + + renderedTemplate = await layout(renderedTemplate, page.metadata, page.metadata.layout!); + + util.write(path.join(basePath, pageNum.toString(), "index.html"), renderedTemplate); + + if (pageNum == 1) { + util.write(path.join(basePath, "index.html"), renderedTemplate); + } + } +} \ No newline at end of file diff --git a/lib/generate/posts.ts b/lib/generate/posts.ts new file mode 100644 index 0000000..310772f --- /dev/null +++ b/lib/generate/posts.ts @@ -0,0 +1,47 @@ +import { promises as fs } from "fs"; +import path from "path"; +import dateFns from "date-fns"; +import slugify from "@sindresorhus/slugify"; +import * as util from "../util"; +import * as metadata from "../metadata"; +import { Page, PostMetadata } from "../metadata"; +import * as markdown from "../markdown"; +import layout from "../layout"; + +export default async function posts(): Promise { + const posts: Page[] = []; + + const files = await fs.readdir("site/posts"); + for (const f of files) { + let page = await metadata.get(path.join("site/posts", f)); + + if (!(page.metadata).permalink) { + let postMeta = page.metadata; + postMeta.date = dateFns.parse(postMeta.date); + postMeta.slug = postMeta.slug || slugify(postMeta.title); + postMeta.permalink = `/${postMeta.category}/${postMeta.date.getFullYear()}/${postMeta.slug}/`; + } + + if (page.metadata.source && page.metadata.source!.endsWith(".md")) { + page.text = markdown.render(page.text); + } + + if (!(page.metadata).excerpt) { + const parts = page.text.split(""); + (page.metadata).excerpt = parts[0]; + } + + const renderedText = await layout(page.text, page.metadata, page.metadata.layout || "article.html.ejs"); + + let dest = page.metadata.permalink; + if (dest.endsWith("/")) { + dest += "index.html"; + } + + util.write(dest, renderedText); + + posts.push(page); + } + + return posts; +} \ No newline at end of file diff --git a/lib/generate/redirects.ts b/lib/generate/redirects.ts new file mode 100644 index 0000000..f61747e --- /dev/null +++ b/lib/generate/redirects.ts @@ -0,0 +1,32 @@ +import ejs from "ejs"; +import * as metadata from "../metadata"; +import { Page } from "../metadata"; +import layout from "../layout"; +import * as util from "../util"; + +async function generateRedirect(oldPermalink: string, newPermalink: string) { + const page = await metadata.get("site/redirect.html.ejs"); + + page.text = ejs.render(page.text, { + metadata: page.metadata, + newPermalink + }, { + filename: "site/redirect.html.ejs" + }); + + page.text = await layout(page.text, page.metadata, page.metadata.layout!); + + if (oldPermalink.endsWith("/")) { + oldPermalink += "index.html"; + } + + util.write(oldPermalink, page.text); +} + +export default async function redirects(posts: Page[]) { + for (const post of posts) { + if (post.metadata.oldPermalink) { + await generateRedirect(post.metadata.oldPermalink, post.metadata.permalink); + } + } +} \ No newline at end of file diff --git a/lib/generate/rss.ts b/lib/generate/rss.ts new file mode 100644 index 0000000..0b3bd18 --- /dev/null +++ b/lib/generate/rss.ts @@ -0,0 +1,38 @@ +import { promises as fs } from "fs"; +import ejs from "ejs"; +import path from "path"; +import { Page, PostMetadata } from "../metadata"; +import * as util from "../util"; +import { TutorialSeries } from "./tutorials"; + +async function generateFeed(posts: Page[], permalink: string, category?: string) { + posts = posts.sort((a, b) => { + const aDate = (a.metadata).date; + const bDate = (b.metadata).date; + return bDate.getTime() - aDate.getTime(); + }); + + const dest = path.join(permalink, "feed.xml"); + + let text = (await fs.readFile("site/feed.xml.ejs")).toString(); + text = ejs.render(text, { + posts, + category, + permalink, + feedPath: dest + }, { + filename: "site/feed.xml.ejs" + }); + + util.write(dest, text); +} + +export default async function rss(posts: Page[], categories: Map, tutorials: TutorialSeries[]) { + generateFeed(posts, "/"); + categories.forEach((posts, category) => { + generateFeed(posts, `/${category}/`, category); + }); + tutorials.forEach(series => { + generateFeed(series.posts, `/tutorials/${series.series}/`, series.seriesName); + }); +} \ No newline at end of file diff --git a/lib/generate/tutorials.ts b/lib/generate/tutorials.ts new file mode 100644 index 0000000..eaec195 --- /dev/null +++ b/lib/generate/tutorials.ts @@ -0,0 +1,118 @@ +import { promises as fs } from "fs"; +import path from "path"; +import dateFns from "date-fns"; +import formatDate from "date-fns/format"; +import slugify from "@sindresorhus/slugify"; +import { Page, PostMetadata } from "../metadata"; +import * as metadata from "../metadata"; +import * as markdown from "../markdown"; +import layout from "../layout"; +import * as util from "../util"; +import ejs from "ejs"; + +async function generateTutorials(group: string): Promise { + const tutorials: Page[] = []; + + const files = await fs.readdir(`site/tutorials/${group}`); + for (const f of files) { + let page = await metadata.get(path.join("site/tutorials", group, f)); + + if (!(page.metadata).permalink) { + let postMeta = page.metadata; + postMeta.date = dateFns.parse(postMeta.date); + postMeta.slug = postMeta.slug || slugify(postMeta.title); + postMeta.permalink = `/tutorials/${group}/${postMeta.slug}/`; + } + + if (page.metadata.source && page.metadata.source!.endsWith(".md")) { + page.text = markdown.render(page.text); + } + + const renderedText = await layout(page.text, page.metadata, page.metadata.layout || "article.html.ejs"); + + let dest = page.metadata.permalink; + if (dest.endsWith("/")) { + dest += "index.html"; + } + + util.write(dest, renderedText); + + tutorials.push(page); + } + + return tutorials; +} + +export interface TutorialSeries { + index: Page; + posts: Page[]; + series: string; + seriesName: string; +} + +interface TutorialIndexMetadata extends PostMetadata { + lastUpdated?: Date; + group?: string; +} + +async function generateTutorialsAndIndex(group: string, title: string): Promise { + let tutorials = await generateTutorials(group); + tutorials = tutorials.sort((a, b) => { + const aDate = (a.metadata).date; + const bDate = (b.metadata).date; + return aDate.getTime() - bDate.getTime(); + }); + + const page = await metadata.get("site/tutorial-series.html.ejs"); + + (page.metadata).permalink = `/tutorials/${group}/`; + (page.metadata).title = title; + (page.metadata).group = group; + (page.metadata).lastUpdated = (tutorials[tutorials.length - 1].metadata).date + + page.text = ejs.render(page.text, { + tutorials, + metadata: page.metadata, + formatDate + }, { + filename: "site/tutorial-series.html.ejs" + }); + + page.text = await layout(page.text, page.metadata, page.metadata.layout!); + + util.write(path.join("tutorials", group, "index.html"), page.text); + + return { + index: page, + posts: tutorials, + series: group, + seriesName: title + }; +} + +async function generateIndex(allSeries: TutorialSeries[]) { + const page = await metadata.get("site/tutorials.html.ejs"); + + page.text = ejs.render(page.text, { + allSeries, + formatDate + }, { + filename: "site/tutorials.html.ejs" + }); + + page.text = await layout(page.text, page.metadata, page.metadata.layout!); + + util.write("tutorials/index.html", page.text); +} + +export default async function tutorials(): Promise { + const series = [ + await generateTutorialsAndIndex("forge-modding-1102", "Forge Mods for 1.10.2"), + await generateTutorialsAndIndex("forge-modding-1112", "Forge Mods for 1.11.2"), + await generateTutorialsAndIndex("forge-modding-112", "Forge Mods for 1.12") + ]; + + generateIndex(series); + + return series; +} \ No newline at end of file diff --git a/lib/index.ts b/lib/index.ts new file mode 100644 index 0000000..2ce7b3d --- /dev/null +++ b/lib/index.ts @@ -0,0 +1,16 @@ +import generators = require("./generate"); + +async function generate() { + generators.css(); + generators.missing(); + + const tutorials = await generators.tutorials(); + + const posts = await generators.posts(); + generators.homepage(posts); + generators.redirects(posts); + const categories = await generators.categories(posts); + generators.rss(posts, categories, tutorials); +} + +generate(); \ No newline at end of file diff --git a/lib/layout.ts b/lib/layout.ts new file mode 100644 index 0000000..d545d0a --- /dev/null +++ b/lib/layout.ts @@ -0,0 +1,22 @@ +import path from "path"; +import ejs from "ejs"; +import formatDate from "date-fns/format"; +import * as metadata from "./metadata"; +import { Metadata } from "./metadata"; + +export default async function layout(text: string, pageMetadata: Metadata, layoutPath: string): Promise { + const layoutFile = path.join("site/layouts", layoutPath); + let layoutPage = await metadata.get(layoutFile); + text = ejs.render(layoutPage.text, { + content: text, + metadata: pageMetadata, + formatDate + }, { + filename: layoutFile, + }); + if (layoutPage.metadata.layout) { + return await layout(text, pageMetadata, layoutPage.metadata.layout); + } else { + return text; + } +} \ No newline at end of file diff --git a/lib/markdown.ts b/lib/markdown.ts new file mode 100644 index 0000000..1e212b3 --- /dev/null +++ b/lib/markdown.ts @@ -0,0 +1,11 @@ +import MarkdownIt from "markdown-it"; +import * as util from "./util"; + +const md = new MarkdownIt({ + highlight: util.highlight, + html: true +}); + +export function render(text: string): string { + return md.render(text); +} \ No newline at end of file diff --git a/lib/metadata.ts b/lib/metadata.ts new file mode 100644 index 0000000..4e522ff --- /dev/null +++ b/lib/metadata.ts @@ -0,0 +1,35 @@ +import { promises as fs } from "fs"; + +export interface Page { + text: string; + metadata: Metadata; +} + +export interface Metadata { + permalink: string; + source?: string; + layout?: string; + oldPermalink?: string; +} + +export interface PostMetadata extends Metadata { + title: string; + slug: string; + category: string; + date: string | Date; + excerpt?: string; +} + +export async function get(path: string): Promise { + let text = (await fs.readFile(path)).toString(); + let metadata = { + source: path + } as Metadata; + if (text.startsWith("```")) { + const parts = text.split("```"); + text = parts.slice(2).join("```"); + const configure = new Function("metadata", parts[1]); + configure(metadata); + } + return {text, metadata}; +} \ No newline at end of file diff --git a/lib/util.ts b/lib/util.ts new file mode 100644 index 0000000..2e3c5c4 --- /dev/null +++ b/lib/util.ts @@ -0,0 +1,35 @@ +import { promises as fs } from "fs"; +import path from "path"; +import hljs from "highlight.js"; + +export async function write(filePath: string, data: any) { + const dest = path.join("out", filePath); + await fs.mkdir(path.dirname(dest), { + recursive: true + }); + await fs.writeFile(dest, data); +} + +export function highlight(source: string, language?: string): string { + const res = language ? hljs.highlight(language, source) : hljs.highlightAuto(source); + const highlighted = res.value; + return `
${highlighted}
`; +} + +interface Chunk { + chunk: T[]; + index: number; +} + +export function chunk(array: T[], size: number): Chunk[] { + const chunks: Chunk[] = []; + + for (let i = 0; i < array.length; i += size) { + chunks.push({ + chunk: array.slice(i, i + size), + index: i / size + }); + } + + return chunks; +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..9fb8b23 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1615 @@ +{ + "name": "shadowfacts.net", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@sindresorhus/slugify": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/slugify/-/slugify-0.6.0.tgz", + "integrity": "sha512-m6smRWGuY0kr0oRdfuTNHWvtBlgtr/ixSa9xiGzFtRjXHghQIlf8s8ZKPWSXj/KraaYuvI//bVBEcncIMzjxVg==", + "requires": { + "escape-string-regexp": "^1.0.5", + "lodash.deburr": "^4.1.0" + } + }, + "@types/ejs": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-2.6.1.tgz", + "integrity": "sha512-WD8015V8Y+uDfFIjiVHU7t9SgHptqTGGN8w0A2LcrL0NLtqColM15cswkHZMUfodyuTf35sup8vW0hpWRHu0dQ==", + "dev": true + }, + "@types/highlight.js": { + "version": "9.12.3", + "resolved": "https://registry.npmjs.org/@types/highlight.js/-/highlight.js-9.12.3.tgz", + "integrity": "sha512-pGF/zvYOACZ/gLGWdQH8zSwteQS1epp68yRcVLJMgUck/MjEn/FBYmPub9pXT8C1e4a8YZfHo1CKyV8q1vKUnQ==", + "dev": true + }, + "@types/linkify-it": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-2.0.4.tgz", + "integrity": "sha512-9o5piu3tP6DwqT+Cyf7S3BitsTc6Cl0pCPKUhIE5hzQbtueiBXdtBipTLLvaGfT11/8XHRmsagu4YfBesTaiCA==", + "dev": true + }, + "@types/markdown-it": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-0.0.7.tgz", + "integrity": "sha512-WyL6pa76ollQFQNEaLVa41ZUUvDvPY+qAUmlsphnrpL6I9p1m868b26FyeoOmo7X3/Ta/S9WKXcEYXUSHnxoVQ==", + "dev": true, + "requires": { + "@types/linkify-it": "*" + } + }, + "@types/node": { + "version": "10.12.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", + "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==", + "dev": true + }, + "@types/node-sass": { + "version": "3.10.32", + "resolved": "https://registry.npmjs.org/@types/node-sass/-/node-sass-3.10.32.tgz", + "integrity": "sha1-spbM5xRP+rd7hAkMqtTx5Lvqjgk=", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/sindresorhus__slugify": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/sindresorhus__slugify/-/sindresorhus__slugify-0.6.0.tgz", + "integrity": "sha512-e67QA1mn1St20y/Ews9EQjUXvfrhZSoD9mqfgQ1LjaSip3zjFwyfiMOdyD6OeBQsuwabTML4ytKyn+PcOwblKA==", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "ajv": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.2.tgz", + "integrity": "sha512-FBHEW6Jf5TB9MGBgUUA9XHkTbjXYfAUjY43ACMfmdMRHniyoMHjHjzD50OK8LGDWQwp4rWEsIq5kEqq7rvIM1g==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "requires": { + "inherits": "~2.0.0" + } + }, + "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" + } + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", + "dev": true + }, + "combined-stream": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "corser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", + "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=", + "dev": true + }, + "cross-spawn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", + "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "requires": { + "array-find-index": "^1.0.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "date-fns": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", + "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==" + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ecstatic": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-3.3.0.tgz", + "integrity": "sha512-EblWYTd+wPIAMQ0U4oYJZ7QBypT9ZUIwpqli0bKDjeIIQnXDBK2dXtZ9yzRCOlkW1HkO8gn7/FxLK1yPIW17pw==", + "dev": true, + "requires": { + "he": "^1.1.1", + "mime": "^1.6.0", + "minimist": "^1.1.0", + "url-join": "^2.0.5" + } + }, + "ejs": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz", + "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==" + }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "eventemitter3": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", + "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", + "dev": true + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "follow-redirects": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.6.1.tgz", + "integrity": "sha512-t2JCjbzxQpWvbhts3l6SH1DKzSrx8a+SsaVf4h6bG4kOXUuPYS/kg2Lr4gQSb7eemaHqJkOThF1BGyjlUkO1GQ==", + "dev": true, + "requires": { + "debug": "=3.1.0" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "requires": { + "globule": "^1.0.0" + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "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" + } + }, + "globule": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz", + "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", + "requires": { + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" + } + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "highlight.js": { + "version": "9.13.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.13.1.tgz", + "integrity": "sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A==" + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" + }, + "http-proxy": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", + "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", + "dev": true, + "requires": { + "eventemitter3": "^3.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-server": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/http-server/-/http-server-0.11.1.tgz", + "integrity": "sha512-6JeGDGoujJLmhjiRGlt8yK8Z9Kl0vnl/dQoQZlc4oeqaUoAKQg94NILLfrY3oWzSyFaQCVNTcKE5PZ3cH8VP9w==", + "dev": true, + "requires": { + "colors": "1.0.3", + "corser": "~2.0.0", + "ecstatic": "^3.0.0", + "http-proxy": "^1.8.1", + "opener": "~1.4.0", + "optimist": "0.6.x", + "portfinder": "^1.0.13", + "union": "~0.4.3" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "in-publish": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", + "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=" + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "requires": { + "repeating": "^2.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.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "requires": { + "builtin-modules": "^1.0.0" + } + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "js-base64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.0.tgz", + "integrity": "sha512-wlEBIZ5LP8usDylWbDNhKPEFVFdI5hCHpnVoT/Ysvoi/PRhJENm/Rlh9TvjYB38HFfKZN7OzEbRjmjvLkFw11g==" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "^1.0.0" + } + }, + "linkify-it": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.1.0.tgz", + "integrity": "sha512-4REs8/062kV2DSHxNfq5183zrqXMl7WP0WzABH9IeJI+NLm429FgE1PDecltYfnOoFDFlZGh2T8PfZn0r+GTRg==", + "requires": { + "uc.micro": "^1.0.1" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + }, + "lodash.deburr": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/lodash.deburr/-/lodash.deburr-4.1.0.tgz", + "integrity": "sha1-3bG7s+8HRYwBd7oH3hRCLLAz/5s=" + }, + "lodash.mergewith": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz", + "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==" + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.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" + } + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" + }, + "markdown-it": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", + "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==", + "requires": { + "argparse": "^1.0.7", + "entities": "~1.1.1", + "linkify-it": "^2.0.0", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + } + }, + "mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=" + }, + "meow": { + "version": "3.7.0", + "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "mime-db": { + "version": "1.37.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", + "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==" + }, + "mime-types": { + "version": "2.1.21", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", + "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", + "requires": { + "mime-db": "~1.37.0" + } + }, + "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" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "nan": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", + "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==" + }, + "node-gyp": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "semver": { + "version": "5.3.0", + "resolved": "http://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" + } + } + }, + "node-sass": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.11.0.tgz", + "integrity": "sha512-bHUdHTphgQJZaF1LASx0kAviPH7sGlcyNhWade4eVIpFp6tsn7SV8xNMTbsQFpEV9VXpnwTTnNYlfsZXgGgmkA==", + "requires": { + "async-foreach": "^0.1.3", + "chalk": "^1.1.1", + "cross-spawn": "^3.0.0", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "in-publish": "^2.0.0", + "lodash.assign": "^4.2.0", + "lodash.clonedeep": "^4.3.2", + "lodash.mergewith": "^4.6.0", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.10.0", + "node-gyp": "^3.8.0", + "npmlog": "^4.0.0", + "request": "^2.88.0", + "sass-graph": "^2.2.4", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "requires": { + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "opener": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.4.3.tgz", + "integrity": "sha1-XG2ixdflgx6P+jlklQ+NZnSskLg=", + "dev": true + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "minimist": { + "version": "0.0.10", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", + "dev": true + } + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "http://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-locale": { + "version": "1.4.0", + "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "requires": { + "lcid": "^1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pify": { + "version": "2.3.0", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "portfinder": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.20.tgz", + "integrity": "sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw==", + "dev": true, + "requires": { + "async": "^1.5.2", + "debug": "^2.2.0", + "mkdirp": "0.5.x" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "psl": { + "version": "1.1.31", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", + "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==" + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "requires": { + "is-finite": "^1.0.0" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sass-graph": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", + "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", + "requires": { + "glob": "^7.0.0", + "lodash": "^4.0.0", + "scss-tokenizer": "^0.2.3", + "yargs": "^7.0.0" + } + }, + "scss-tokenizer": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", + "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", + "requires": { + "js-base64": "^2.1.8", + "source-map": "^0.4.2" + } + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "source-map": { + "version": "0.4.4", + "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "requires": { + "amdefine": ">=0.0.4" + } + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz", + "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==" + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "sshpk": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.0.tgz", + "integrity": "sha512-Zhev35/y7hRMcID/upReIvRse+I9SVhyVre/KTJSJQWMz3C3+G+HpO7m1wK/yckEtujKZ7dS4hkVxAnmHaIGVQ==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "stdout-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", + "requires": { + "readable-stream": "^2.0.1" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "requires": { + "get-stdin": "^4.0.1" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "http://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, + "tar": { + "version": "2.2.1", + "resolved": "http://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "requires": { + "block-stream": "*", + "fstream": "^1.0.2", + "inherits": "2" + } + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=" + }, + "true-case-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", + "requires": { + "glob": "^7.1.2" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "uc.micro": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.5.tgz", + "integrity": "sha512-JoLI4g5zv5qNyT09f4YAvEZIIV1oOjqnewYg5D38dkQljIzpPT296dbIGvKro3digYI1bkb7W6EP1y4uDlmzLg==" + }, + "union": { + "version": "0.4.6", + "resolved": "http://registry.npmjs.org/union/-/union-0.4.6.tgz", + "integrity": "sha1-GY+9rrolTniLDvy2MLwR8kopWeA=", + "dev": true, + "requires": { + "qs": "~2.3.3" + }, + "dependencies": { + "qs": { + "version": "2.3.3", + "resolved": "http://registry.npmjs.org/qs/-/qs-2.3.3.tgz", + "integrity": "sha1-6eha2+ddoLvkyOBHaghikPhjtAQ=", + "dev": true + } + } + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "url-join": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz", + "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + } + } + }, + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "requires": { + "camelcase": "^3.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..eae3d85 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "shadowfacts.net", + "version": "1.0.0", + "scripts": { + "generate": "tsc -p . && node built/index.js", + "serve": "npm run generate && http-server out" + }, + "dependencies": { + "@sindresorhus/slugify": "^0.6.0", + "date-fns": "^1.30.1", + "ejs": "^2.6.1", + "highlight.js": "^9.13.1", + "markdown-it": "^8.4.2", + "node-sass": "^4.11.0" + }, + "devDependencies": { + "@types/ejs": "^2.6.1", + "@types/highlight.js": "^9.12.3", + "@types/markdown-it": "0.0.7", + "@types/node-sass": "^3.10.32", + "@types/sindresorhus__slugify": "^0.6.0", + "http-server": "^0.11.1" + } +} diff --git a/site/404.html.ejs b/site/404.html.ejs new file mode 100644 index 0000000..149ea54 --- /dev/null +++ b/site/404.html.ejs @@ -0,0 +1,14 @@ +``` +metadata.title = "Not Found" +metadata.layout = "default.html.ejs" +``` + + \ No newline at end of file diff --git a/site/category.html.ejs b/site/category.html.ejs new file mode 100644 index 0000000..6d43538 --- /dev/null +++ b/site/category.html.ejs @@ -0,0 +1,48 @@ +``` +metadata.layout = "default.html.ejs" +``` + +
+

<%= category %> posts

+

+ Subscribe to just <%= category %> posts via RSS. +

+ + <% for (const post of posts) { %> + + <% } %> +
+ + \ No newline at end of file diff --git a/site/css/dark.scss b/site/css/dark.scss new file mode 100644 index 0000000..e779cd7 --- /dev/null +++ b/site/css/dark.scss @@ -0,0 +1,25 @@ +:root { + --accent-color: #f9c72f; + --content-background-color: #111; + --shadow-color: #151515; + --ui-background-color: #111; + --ui-text-color: white; + --secondary-ui-text-color: #999; + --content-text-color: #ddd; + + // Syntax highlighting + --atom-base: #282c34; + --atom-mono-1: #abb2bf; + --atom-mono-2: #818896; + --atom-mono-3: #5c6370; + --atom-hue-1: #56b6c2; + --atom-hue-2: #61aeee; + --atom-hue-3: #c678dd; + --atom-hue-4: #98c379; + --atom-hue-5: #e06c75; + --atom-hue-5-2: #be5046; + --atom-hue-6: #d19a66; + --atom-hue-6-2: #e6c07b; +} + +<%- include("main.scss") %> \ No newline at end of file diff --git a/site/css/light.scss b/site/css/light.scss new file mode 100644 index 0000000..03df970 --- /dev/null +++ b/site/css/light.scss @@ -0,0 +1,25 @@ +:root { + --accent-color: #0638d0; + --content-background-color: white; + --shadow-color: #f7f7f7; + --ui-background-color: white; + --ui-text-color: black; + --secondary-ui-text-color: #666; + --content-text-color: #222; + + // Syntax highlighting + --atom-base: #fafafa; + --atom-mono-1: #383a42; + --atom-mono-2: #686b77; + --atom-mono-3: #a0a1a7; + --atom-hue-1: #0184bb; + --atom-hue-2: #4078f2; + --atom-hue-3: #a626a4; + --atom-hue-4: #50a14f; + --atom-hue-5: #e45649; + --atom-hue-5-2: #c91243; + --atom-hue-6: #986801; + --atom-hue-6-2: #c18401; +} + +<%- include("main.scss") %> \ No newline at end of file diff --git a/site/css/main.scss b/site/css/main.scss new file mode 100644 index 0000000..dc625a4 --- /dev/null +++ b/site/css/main.scss @@ -0,0 +1,357 @@ +<%- include("normalize.css") %> +<%- include("syntax-highlighting.css") %> + +// Fonts +$sansSerif: Lucida Grande, Arial, sans-serif; +$serif: Georgia, serif; +$monospace: SF Mono, Monaco, Courier New, monospace; + +// General +body { + background-color: var(--content-background-color); + font-family: $sansSerif; + font-size: 16px; + line-height: 1.5; + color: var(--ui-text-color); +} + +.container { + margin: 0 auto; +} + +.main { + max-width: 720px; + margin: 0 auto; + + .page-heading { + max-width: 720px; + margin: 20px auto; + margin-bottom: 0; + color: var(--content-text-color); + } + + .rss { + margin-top: 0; + font-size: 14px; + font-weight: lighter; + color: var(--secondary-ui-text-color); + } +} + +article { + // max-width: 720px; + // margin: 0 auto; + margin-bottom: 75px; + color: var(--content-text-color); + + &::after { + content: ""; + width: 100%; + max-width: 720px; + height: 1px; + background-image: linear-gradient(to right, var(--secondary-ui-text-color), var(--shadow-color)); + position: absolute; + } + + .article-title { + margin-top: 20px; + margin-bottom: 0; + + > a { + color: var(--content-text-color); + text-decoration: none; + transition: 0.3s ease all; + + &:hover { + color: var(--accent-color); + } + } + } + + .article-meta { + margin-top: 0; + font-size: 14px; + font-weight: lighter; + color: var(--secondary-ui-text-color); + } + + .article-content { + font-family: $serif; + font-size: 18px; + + h1, h2, h3, h4, h5, h6 { + font-family: $sansSerif; + } + } +} + +.search { + margin: 100px auto; + text-align: center; + + h3 { + position: relative; + color: var(--content-text-color); + line-height: 1.3; + text-align: center; + margin: 0; + padding: 0 2rem; + } + + input#q { + display: block; + width: 75%; + margin: 10px auto; + padding: 4px; + background-color: var(--content-background-color); + border: 1px solid var(--accent-color); + font-size: 1rem; + color: var(--content-text-color); + } + + input[type=submit] { + display: block; + margin: 10px auto; + background-color: var(--ui-background-color); + border: 1px solid var(--accent-color); + color: var(--accent-color); + line-height: 2rem; + padding: 0 1rem; + text-decoration: none; + font-weight: bold; + text-transform: uppercase; + + -webkit-transition: 0.3s ease-out; + transition: 0.3s ease-out; + + &:hover { + background-color: var(--accent-color); + color: var(--ui-background-color); + cursor: pointer; + } + } +} + +.icon > svg { + display: inline-block; + width: 16px; + height: 16px; + vertical-align: middle; + color: grey; +} + +a { + color: var(--accent-color); + + &.fancy-link { + position: relative; + color: var(--ui-text-color); + text-decoration: none; + transition: 0.3s ease all; + + &::before, &::after { + position: absolute; + transition: 0.3s ease all; + font-family: $monospace; + color: transparent; + } + + &::before { + content: "{"; + left: 0.5em; + } + + &::after { + content: "}"; + right: 0.5em; + } + + &:hover { + color: var(--accent-color); + + &::before, &::after { + color: var(--accent-color); + } + + &::before { + left: -0.75em; + } + + &::after { + right: -0.75em; + } + } + } +} + +pre { + overflow: scroll; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; +} + +pre, code { + font-family: $monospace; + font-size: 16px; +} + +figure { + img { + display: block; + margin: 0 auto; + } + figcaption { + font-family: $sansSerif; + font-size: 16px; + font-style: italic; + color: var(--secondary-ui-text-color); + text-align: center; + } +} + +// Header +.site-header { + padding: 20px 0; + background-color: var(--ui-background-color); + font-size: 16px; + box-shadow: 0 10px 15px var(--shadow-color); + + .container { + position: relative; + } + + .site-title { + margin: 0; + font-size: 2em; + font-variant: small-caps; + } + + .site-description { + color: var(--secondary-ui-text-color); + font-variant: small-caps; + margin-top: 0; + } + + .site-nav ul { + padding: 0; + margin: 0; + + li { + list-style: none; + display: inline; + margin-right: 1em; + font-variant: small-caps; + font-weight: bold; + } + } + + @media (min-width: 992px) { + .site-nav { + position: absolute; + right: 0; + bottom: 0; + } + } +} + +// Footer +.site-footer { + padding: 20px 0; + background-color: var(--ui-background-color); + font-size: 16px; + box-shadow: 0 -10px 15px var(--shadow-color); + + .container { + position: relative; + } + + .site-title { + margin: 0; + font-variant: small-caps; + font-size: 1.5em; + } + + .social-links ul { + padding: 0; + margin: 0; + + li { + list-style: none; + display: inline; + margin-right: 1em; + font-variant: small-caps; + font-weight: bold; + } + } + + @media (min-width: 992px) { + .social-links { + position: absolute; + right: 0; + top: 0; + } + } +} + +// Pagination +.pagination { + text-align: center; + + .pagination-link { + color: var(--accent-color); + + a { + text-decoration: none; + + span:not(.arrow) { + text-decoration: underline; + } + } + + .arrow-left { + display: inline-block; + width: 0.5em; + height: 0.5em; + margin-right: -5px; + border-left: 2px solid var(--accent-color); + border-bottom: 2px solid var(--accent-color); + transform: rotate(45deg); + } + .arrow-right { + display: inline-block; + width: 0.5em; + height: 0.5em; + margin-left: -5px; + border-right: 2px solid var(--accent-color); + border-bottom: 2px solid var(--accent-color); + transform: rotate(-45deg); + } + } +} + +// Media Queries +@media (min-width: 540px) { + .container { + max-width: 540px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 720px; + } +} + +@media (min-width: 992px) { + .container { + max-width: 960px; + } +} + +@media (min-width: 1200px) { + .container { + max-width: 1140px; + } +} \ No newline at end of file diff --git a/site/css/normalize.css b/site/css/normalize.css new file mode 100644 index 0000000..e537875 --- /dev/null +++ b/site/css/normalize.css @@ -0,0 +1,341 @@ +/*! normalize.css v8.0.0 | MIT License | github.com/necolas/normalize.css */ + +/* Document + ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in iOS. + */ + + html { + line-height: 1.15; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ + } + + /* Sections + ========================================================================== */ + + /** + * Remove the margin in all browsers. + */ + + body { + margin: 0; + } + + /** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + + h1 { + font-size: 2em; + margin: 0.67em 0; + } + + /* Grouping content + ========================================================================== */ + + /** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + + hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ + } + + /** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + + pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ + } + + /* Text-level semantics + ========================================================================== */ + + /** + * Remove the gray background on active links in IE 10. + */ + + a { + background-color: transparent; + } + + /** + * 1. Remove the bottom border in Chrome 57- + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + + abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ + } + + /** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + + b, + strong { + font-weight: bolder; + } + + /** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + + code, + kbd, + samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ + } + + /** + * Add the correct font size in all browsers. + */ + + small { + font-size: 80%; + } + + /** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + + sub, + sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; + } + + sub { + bottom: -0.25em; + } + + sup { + top: -0.5em; + } + + /* Embedded content + ========================================================================== */ + + /** + * Remove the border on images inside links in IE 10. + */ + + img { + border-style: none; + } + + /* Forms + ========================================================================== */ + + /** + * 1. Change the font styles in all browsers. + * 2. Remove the margin in Firefox and Safari. + */ + + button, + input, + optgroup, + select, + textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ + } + + /** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + + button, + input { /* 1 */ + overflow: visible; + } + + /** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + + button, + select { /* 1 */ + text-transform: none; + } + + /** + * Correct the inability to style clickable types in iOS and Safari. + */ + + button, + [type="button"], + [type="reset"], + [type="submit"] { + -webkit-appearance: button; + } + + /** + * Remove the inner border and padding in Firefox. + */ + + button::-moz-focus-inner, + [type="button"]::-moz-focus-inner, + [type="reset"]::-moz-focus-inner, + [type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; + } + + /** + * Restore the focus styles unset by the previous rule. + */ + + button:-moz-focusring, + [type="button"]:-moz-focusring, + [type="reset"]:-moz-focusring, + [type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; + } + + /** + * Correct the padding in Firefox. + */ + + fieldset { + padding: 0.35em 0.75em 0.625em; + } + + /** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + + legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ + } + + /** + * Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + + progress { + vertical-align: baseline; + } + + /** + * Remove the default vertical scrollbar in IE 10+. + */ + + textarea { + overflow: auto; + } + + /** + * 1. Add the correct box sizing in IE 10. + * 2. Remove the padding in IE 10. + */ + + [type="checkbox"], + [type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ + } + + /** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + + [type="number"]::-webkit-inner-spin-button, + [type="number"]::-webkit-outer-spin-button { + height: auto; + } + + /** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + + [type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ + } + + /** + * Remove the inner padding in Chrome and Safari on macOS. + */ + + [type="search"]::-webkit-search-decoration { + -webkit-appearance: none; + } + + /** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + + ::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ + } + + /* Interactive + ========================================================================== */ + + /* + * Add the correct display in Edge, IE 10+, and Firefox. + */ + + details { + display: block; + } + + /* + * Add the correct display in all browsers. + */ + + summary { + display: list-item; + } + + /* Misc + ========================================================================== */ + + /** + * Add the correct display in IE 10+. + */ + + template { + display: none; + } + + /** + * Add the correct display in IE 10. + */ + + [hidden] { + display: none; + } \ No newline at end of file diff --git a/site/css/syntax-highlighting.css b/site/css/syntax-highlighting.css new file mode 100644 index 0000000..5f74323 --- /dev/null +++ b/site/css/syntax-highlighting.css @@ -0,0 +1,81 @@ +/* +Atom One color scheme by Daniel Gamage +Modified to use colors from CSS vars, defined in theme.scss +*/ + +.hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + color: var(--atom-mono-1); + background: var(--atom-base); +} + +.hljs-comment, +.hljs-quote { + color: var(--atom-mono-3); + font-style: italic; +} + +.hljs-doctag, +.hljs-keyword, +.hljs-formula { + color: var(--atom-hue-3); +} + +.hljs-section, +.hljs-name, +.hljs-selector-tag, +.hljs-deletion, +.hljs-subst { + color: var(--atom-hue-5); +} + +.hljs-literal { + color: var(--atom-hue-1); +} + +.hljs-string, +.hljs-regexp, +.hljs-addition, +.hljs-attribute, +.hljs-meta-string { + color: var(--atom-hue-4); +} + +.hljs-built_in, +.hljs-class .hljs-title { + color: var(--atom-hue-6-2); +} + +.hljs-attr, +.hljs-variable, +.hljs-template-variable, +.hljs-type, +.hljs-selector-class, +.hljs-selector-attr, +.hljs-selector-pseudo, +.hljs-number { + color: var(--atom-hue-6); +} + +.hljs-symbol, +.hljs-bullet, +.hljs-link, +.hljs-meta, +.hljs-selector-id, +.hljs-title { + color: var(--atom-hue-2); +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} + +.hljs-link { + text-decoration: underline; +} \ No newline at end of file diff --git a/site/feed.xml.ejs b/site/feed.xml.ejs new file mode 100644 index 0000000..3bfd9de --- /dev/null +++ b/site/feed.xml.ejs @@ -0,0 +1,32 @@ + + + Shadowfacts<% if (category) { %> (<%= category %>)<% } %> + + <% if (category) { %> + Only <%= category %> posts. + <% } else { %> + Just my various ramblings. + <% } %> + + + + https://shadowfacts.net<%= feedPath %> + <%= new Date().toISOString() %> + + Shadowfacts + + <% for (const post of posts) { %> + + https://shadowfacts.net<%= post.metadata.permalink %> + <%= post.metadata.title %> + <%= post.metadata.date.toISOString() %> + + <% if (post.metadata.category) { %> + + <% } %> + + ]]> + + <% } %> + \ No newline at end of file diff --git a/site/includes/article-meta.html.ejs b/site/includes/article-meta.html.ejs new file mode 100644 index 0000000..a45ad04 --- /dev/null +++ b/site/includes/article-meta.html.ejs @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/site/index.html.ejs b/site/index.html.ejs new file mode 100644 index 0000000..31512a4 --- /dev/null +++ b/site/index.html.ejs @@ -0,0 +1,44 @@ +``` +metadata.title = "Shadowfacts" +metadata.layout = "default.html.ejs" +``` + +
+ <% for (const post of posts) { %> + + <% } %> +
+ + \ No newline at end of file diff --git a/site/layouts/article.html.ejs b/site/layouts/article.html.ejs new file mode 100644 index 0000000..e83b31a --- /dev/null +++ b/site/layouts/article.html.ejs @@ -0,0 +1,14 @@ +``` +metadata.layout = "default.html.ejs" +``` + +
+
+ +

<%= metadata.title %>

+ <%- include("../includes/article-meta.html.ejs", { metadata }) %> +
+ <%- content %> +
+
+
\ No newline at end of file diff --git a/site/layouts/default.html.ejs b/site/layouts/default.html.ejs new file mode 100644 index 0000000..4cc65fb --- /dev/null +++ b/site/layouts/default.html.ejs @@ -0,0 +1,83 @@ + + + + + + + <%= metadata.title %> + + + + + + +
+ <%- content %> +
+ +
+
+
+

Shadowfacts

+

+ + +

+
+ +
+ + + +
+ + + \ No newline at end of file diff --git a/site/posts/2016-05-05-hello-world.md b/site/posts/2016-05-05-hello-world.md new file mode 100644 index 0000000..559a079 --- /dev/null +++ b/site/posts/2016-05-05-hello-world.md @@ -0,0 +1,7 @@ +``` +metadata.title = "Hello, World!" +metadata.category = "meta" +metadata.date = "2016-05-06 11:13:18 -0400" +``` + +Hello, again, world! Welcome to the third iteration of my website. Originally my site was hosted on GitHub pages and only available at [shadowfacts.github.io](https://shadowfacts.github.io). I wrote a couple of tutorials on using [Forge](http://minecraftforge.net) to mod 1.6.4, but never really finished anything other than super basic setup/recipes. Later, after I got [shadowfacts.net](https://shadowfacts.net), I decided to set up a propper website using [WordPress](https://wordpress.org). I copied over all of the old tutorials from my old GitHub pages site, but never really did anything else with it. After my website being offline for almost a year, I've finally decided to switch back to GitHub for the simplicity (also I <3 [Jekyll](https://jekyllrb.com)). Using Jekyll, I've got a structure in place that I can use to easily publish tutorials in a structured format. There is one tutorial series that I'm currently writing and that is [Forge Mods in 1.9](/tutorials/forge-modding-19/), and hopefully more series will follow. \ No newline at end of file diff --git a/site/posts/2016-05-21-194-porting-spree.md b/site/posts/2016-05-21-194-porting-spree.md new file mode 100644 index 0000000..2e6e280 --- /dev/null +++ b/site/posts/2016-05-21-194-porting-spree.md @@ -0,0 +1,44 @@ +``` +metadata.title = "1.9.4 Porting Spree" +metadata.category = "minecraft" +metadata.date = "2016-05-21 17:47:18 -0400" +metadata.oldPermalink = "/mods/2016/05/21/194-porting-spree/" +``` + +Now that Forge for 1.9.4 is [out](http://files.minecraftforge.net/maven/net/minecraftforge/forge/index_1.9.4.html), I've begun the log and arduous process of porting my mods to 1.9.4 (if by long and arduous, you mean short and trivial). + + + +
+

ShadowMC

+ 3.3.0 +

+ The library mod required by all of my other mods. +

+
+
+

Sleeping Bag

+ 1.2.0 +

+ Adds a simple sleeping bag item that is usable anywhere and doens't set your spawn which makes it quite handy for bringing on adventures. +

+
+
+

Ye Olde Tanks

+ 1.7.0 +

+ Fluid stuff: Fluid barrels, creative fluid barrels, fluid barrel minecarts, infinite water buckets. +

+
+
+

DiscordChat

+ 1.2.0 +

+ Merges a Discord channel with Minecraft chat, primarily intended for servers. +

+
+
+

ShadowTweaks

+ 1.9.0 +

A little tweaks mod with a variety of client/server tweaks.

+
\ No newline at end of file diff --git a/site/posts/2016-06-29-introducing-rtfm.md b/site/posts/2016-06-29-introducing-rtfm.md new file mode 100644 index 0000000..c17b531 --- /dev/null +++ b/site/posts/2016-06-29-introducing-rtfm.md @@ -0,0 +1,12 @@ +``` +metadata.title = "Introducing RTFM" +metadata.category = "minecraft" +metadata.date = "2016-06-29 12:00:00 -0400" +metadata.oldPermalink = "/meta/2016/06/29/introducing-rtfm/" +``` + +[RTFM](https://rtfm.shadowfacts.net/) is the brand new website that will contain the documentation for all of my projects, currently it only contains documentation for MC mods. Like this website, it is [hosted on GitHub](https://github.com/shadowfacts/RTFM) using GitHub pages. + + + +![XKCD #293 RTFM](https://imgs.xkcd.com/comics/rtfm.png) \ No newline at end of file diff --git a/site/posts/2016-06-30-forge-1102-tutorials.md b/site/posts/2016-06-30-forge-1102-tutorials.md new file mode 100644 index 0000000..b568eb6 --- /dev/null +++ b/site/posts/2016-06-30-forge-1102-tutorials.md @@ -0,0 +1,8 @@ +``` +metadata.title = "Forge Modding Tutorials for 1.10.2" +metadata.category = "minecraft" +metadata.date = "2016-06-30 10:35:00 -0400" +metadata.oldPermalink = "/meta/2016/06/30/forge-1102-tutorials/" +``` + +The Forge modding tutorials have all the been [updated to MC 1.10.2](/tutorials/forge-modding-1102/) as has the [GitHub repo](https://github.com/shadowfacts/TutorialMod/). \ No newline at end of file diff --git a/site/posts/2016-07-28-introducing-mirror.md b/site/posts/2016-07-28-introducing-mirror.md new file mode 100644 index 0000000..c6acb7e --- /dev/null +++ b/site/posts/2016-07-28-introducing-mirror.md @@ -0,0 +1,149 @@ +``` +metadata.title = "Introducing Mirror" +metadata.category = "java" +metadata.date = "2016-07-28 16:45:00 -0400" +``` + +Allow me to introduce my latest project, Mirror. Mirror is a [reflection][] library for Java designed to take advantage of the streams, lambdas, and optionals introduced in Java 8. + + + +The source code is publicly available on [GitHub][source] under the MIT license and the JavaDocs are viewable [here][docs]. + +## Installation + +All version of Mirror are [available on my Maven][maven]. + +### Maven +```xml + + + shadowfacts + http://mvn.rx14.co.uk/shadowfacts/ + + + + + net.shadowfacts + Mirror + 1.0.0 + +``` + +### Gradle +```groovy +repositories { + maven { + name "shadowfacts" + url "http://mvn.rx14.co.uk/shadowfacts/" + } +} + +dependencies { + compile group: "net.shadowfacts", name: "Mirror", version: "1.0.0" +} +``` + +## Usage +A couple of simple examples for getting started with Mirror. + +For more complex examples of everything possible with Mirror, you can look at the [unit tests][tests]. + +### General Overview +The `Mirror.of` methods are used to retrieve mirrors on which operations can be performed. The types of mirrors are: + +- [`MirrorClass`][class] +- [`MirrorEnum`][enum] +- [`MirrorConstructor`][constructor] +- [`MirrorMethod`][method] +- [`MirrorField`][field] + +The `Mirror.ofAll` methods are used to create mirror stream wrappers for a given stream/collection/array of reflection objects or mirrors. + +These examples will use the following classes: + +```java +public class Test { + public static String name = "Mirror"; + public static String author; + + public static String reverse(String str) { + return new StringBuilder(str).reverse().toString(); + } +} + +public class Test2 { + public static String name = "Test 2"; + + public static void doSomething() { + } +} +``` + +### Getting Fields +```java +// get the field +Optional optional = Mirror.of(Test.class).field("name"); +// unwrap the optional +MirrorField field = optional.get(); +// get the value of the field +// we pass null as the instance because the field is static +field.get(null); // "Mirror" +``` + +### Setting Fields +```java +// get the field +Optional optional = Mirror.of(Test.class).field("author"); +// unwrap the optional +MirrorField field = optional.get(); +// set the value of the field +// we once again pass null as the instance because the field is static +field.set(null, "Shadowfacts"); +``` + +### Invoking Methods +```java +// get the method using the name and the types of the arguments it accepts +Optional optional = Mirror.of(Test.class).method("reverse", String.class); +// unwrap the optional +MirrorMethod method = optional.get(); +// invoke the method +method.invoke(null, "Mirror"); // "rorriM"; +``` + +### Class Streams +```java +Mirror.ofAllUnwrapped(Test.class, Test2.class) // create the stream of classes + .unwrap() // map the MirrorClasses to their Java versions + .toArray(); // [Test.class, Test2.class] +``` + +### Field Streams +```java +Mirror.ofAllUnwrapped(Test.class, Test2.class) // create the stream of classes + .flatMapToFields() // flat map the classes to their fields + .get(null) // get the value of the fields on null + .toArray(); // ["Mirror", "Shadowfacts", "Tesst 2"] +``` + +### Method Streams +```java +Mirror.ofAllUnwrapped(Test.class, Test2.class) // create the stream of classes + .flatMapToMethods() // flat map the classes to their methods + .filter(m -> Arrays.equals(m.parameterTypes(), new MirrorClass[]{Mirror.of(String.class)})) // filter the methods by which accept only a String + .invoke(null, "Shadowfacts") // invoke them all on nothing, passing in "Shadowfacts" + .toArray(); // ["stcafwodahS"] +``` + + +[reflection]: https://en.wikipedia.org/wiki/Reflection_(computer_programming) +[source]: https://github.com/shadowfacts/Mirror/ +[docs]: https://shadowfacts.net/Mirror/ +[maven]: http://mvn.rx14.co.uk/shadowfacts/net/shadowfacts/Mirror +[tests]: https://github.com/shadowfacts/Mirror/tree/master/src/test/java/net/shadowfacts/mirror +[class]: https://shadowfacts.net/Mirror/net/shadowfacts/mirror/MirrorClass.html +[enum]: https://shadowfacts.net/Mirror/net/shadowfacts/mirror/MirrorEnum.html +[constructor]: https://shadowfacts.net/Mirror/net/shadowfacts/mirror/MirrorConstructor.html +[method]: https://shadowfacts.net/Mirror/net/shadowfacts/mirror/MirrorMethod.html +[field]: https://shadowfacts.net/Mirror/net/shadowfacts/mirror/MirrorField.html \ No newline at end of file diff --git a/site/posts/2016-08-06-kotlin-and-forge.md b/site/posts/2016-08-06-kotlin-and-forge.md new file mode 100644 index 0000000..29d9fd6 --- /dev/null +++ b/site/posts/2016-08-06-kotlin-and-forge.md @@ -0,0 +1,38 @@ +``` +metadata.title = "Kotlin and Minecraft Forge" +metadata.category = "minecraft" +metadata.date = "2016-08-06 16:45:30 -0400" +metadata.oldPermalink = "/forge/2016/08/06/kotlin-and-forge/" +``` + +So, you wanna use [Kotlin][] in your Forge mod? Well there's good news, I've just released [Forgelin][], a fork of [Emberwalker's Forgelin][EWForgelin], a library that provides utilities for using Kotlin with Minecraft/Forge. + +Forgelin provides a Kotlin langauge adapter that allows your main-mod class to be a [`object`][KotlinObject]. In order to use the language adapter, you must specify the `modLanguageAdapter` property in your `@Mod` annotation to be `net.shadowfacts.forgelin.KotlinAdapter`. + + + +Additionally, Forgelin repackages the Kotlin standard library, reflect library, and runtime so that you don't have to, and so that end users don't have to download the 3 megabytes of Kotlin libraries multiple times. + +~~Additionally, Forgelin provides a number of [extensions][KotlinExtensions] (which are viewable [here][ExtensionsList]) for working with Minecraft/Forge.~~ + +~~While you can shade Forgelin, it is not recommended to do so. It will increase your jar size by approximately 3 megabytes (as Forgelin itself includes the entire Kotlin, standard lib, reflect lib, and runtime) and may cause issues with other mods that shade Kotlin or Forgelin. It is recommended that you have your users download Forgelin from [CurseForge][].~~ + +**Update Feb 17, 2017:** + +1. As of Forgelin 1.1.0, the extensions have been moved from Forgelin to [ShadowMC][]. +2. As of Forgelin 1.3.0, Forgelin includes an `@Mod` annotated object. This means: + 1. **Forgelin can no longer be shaded.** + 2. `required-after:forgelin;` can now be used in the `dependencies` field of your `@Mod` annotation for a nicer error message when Forgelin isn't installed. + + +A bare-bones example mod using Forgelin is available [here][example]. + +[Kotlin]: https://kotlinlang.org/ +[Forgelin]: https://github.com/shadowfacts/Forgelin +[EWForgelin]: https://github.com/Emberwalker/Forgelin +[KotlinObject]: https://kotlinlang.org/docs/reference/object-declarations.html +[KotlinExtensions]: https://kotlinlang.org/docs/reference/extensions.html +[ExtensionsList]: https://github.com/shadowfacts/Forgelin/tree/master/src/main/kotlin/net/shadowfacts/forgelin/extensions +[ShadowMC]: https://github.com/shadowfacts/ShadowMC/tree/1.11.2/src/main/kotlin/net/shadowfacts/forgelin/extensions +[CurseForge]: https://minecraft.curseforge.com/projects/shadowfacts-forgelin +[example]: https://github.com/shadowfacts/ForgelinExample diff --git a/site/posts/2016-08-07-the-great-redesign.md b/site/posts/2016-08-07-the-great-redesign.md new file mode 100644 index 0000000..8805f0f --- /dev/null +++ b/site/posts/2016-08-07-the-great-redesign.md @@ -0,0 +1,29 @@ +``` +metadata.title = "The Great Redesign" +metadata.category = "meta" +metadata.date = "2016-08-07 15:39:48 -0400" +``` + +Welcome to the fourth iteration of my website. I'm still using Jekyll, however I've rewritten most of the styles from scratch. This theme is based on the [Hacker theme][HackerHexo] for [Hexo][] which is turn based on the [Hacker WordPress theme][HackerWP] but it has some notable differences. + + + +### 1\. It's built for Jekyll. + +Because Jekyll (and more specifically, GitHub Pages) uses Sass instead of [Styl][] like Hacker, all of the styles had to be rewritten from scratch in SCSS. Most of the original [Minima][] styles were scrapped, except for a couple of code styling details and the footer design. + +### 2\. It has a dark theme + +This is accomplished storing the current them (`dark` or `light`) in a cookie, reading it in the head, and writing a `` element based on the the value of the theme. All the styles are stored in `_sass/theme.scss` and the `css/light.scss` and `css/dark.scss` files store the variable definitions for all the colors used in the theme. Jekyll then compiles the two main SCSS files into two CSS files that each contain [Normalize.css][Normalize], the theme (compiled from the variable definitions), and the [Darcula][RougeDarcula] syntax highlighting theme. + +While this does increase the load time and isn't best practice, I think providing the option of a dark theme (especially when the deafult theme is incredibly light (the majority of the page is pure white (ooh, tripple nested parentheses))) outweights the cost. Besides, when testing locally the entire script loading and executiononly cost 5 miliseconds, completely unnoticable. + +The selector in the third column of the footer simply updates the cookie value based on the checkbox status and reloads the page via `window.location.reload()` triggering the changed theme CSS to be loaded. + +[HackerHexo]: https://github.com/CodeDaraW/Hacker +[Hexo]: https://hexo.io/ +[HackerWP]: https://wordpress.org/themes/hacker/ +[Styl]: https://github.com/tj/styl +[Minima]: https://github.com/jekyll/minima +[Normalize]: https://necolas.github.io/normalize.css/ +[RougeDarcula]: https://github.com/shadowfacts/RougeDarcula diff --git a/site/posts/2016-10-02-type.md b/site/posts/2016-10-02-type.md new file mode 100644 index 0000000..16080c0 --- /dev/null +++ b/site/posts/2016-10-02-type.md @@ -0,0 +1,112 @@ +``` +metadata.title = "Type: A FOSS clone of typing.io" +metadata.category = "misc" +metadata.date = "2016-10-08 17:29:42 -0400" +``` + +**TL;DR**: I made an awesome FOSS clone of [typing.io](https://typing.io) that you can check out at [type.shadowfacts.net](https://type.shadowfacts.net) and the source of which you can see [here](https://github.com/shadowfacts/type). + +I've used [typing.io](https://typing.io) on and off for almost a year now, usually when I'm bored and have nothing else to do. Unfortunately, I recently completed the Java file, the C++ file, and the JavaScript file (that last one took too much time, jQuery has weird coding formatting standards, IMO) meaning I've completed pretty much everything that interests me. Now if you want to upload your own code to type, you have to pay $9.99 _a month_, which, frankly, is ridiculous. $10 a month to be able to upload code to a website only to have more than the 17 default files (one for each langauge) when I could build my own clone. + + + +This is my fourth attempt at building a clone of typing.io, and the first one that's actually been successful. (The first, second, and third all failed because I was trying to make a syntax highlighting engine work with too much custom code.) + +Type uses [CodeMirror](https://codemirror.net/), a fantastic (and very well documented) code editor which handles [syntax highlighting](#syntax-highlighting), [themes](#themes), [cursor handling](#cursor-handling), and [input](#input). + +## Input +Input was one of the first things I worked on. (I wanted to get the very basics working before I got cought up in minor details.) CodeMirorr's normal input method doesn't work for me, because in Type, all the text is in the editor beforehand and the user doesn't actually type it out. The CodeMirror instance is set to `readOnly` mode, making entering or removing text impossible. This is all well and good, but how can you practice typing if you can't type? Well, you don't actually type. The DOM `keypress` and `keydown` events are used to handle character input and handle special key input (return, backspace, tab, and escape) respectively. + +The `keypress` event handler simply moves the cursor one character and marks the typed character as completed. If the character the user typed isn't the character that's in the document they are typing, a CodeMirror [TextMarker](http://codemirror.net/doc/manual.html#markText) with the `invalid` class will be used to display a red error-highlight to the user. These marks are then stored in a 2-dimensional array which is used to check if the user has actully completed the file. + +The `keydown` event is used for handling special key pressed namely, return, backspace, delete, and escape. + +When handling a return press, the code first checks if the user has completed the current line (This is a little bit more complicated than checking if the cursor position is at the end of the line, because Type allows you to skip typing whitespace at the beggining and end of lines because every IDE/editor under the sun handles that for you). Then, the editor moves the cursor to the beggining of the next line (see the previous parenthetical). + +Backspace handling works much the same way, checking if the user is at the begging of the line, and if so, moving to the end of the previous line, or otherwise moving back 1 character. Delete also has a bit of extra functionality specific to Type. Whenever you press delete and the previous character was marked as invalid, the invalid marks needs to A) be cleared from the CodeMirror document and B) removed from the 2D array of invalid marks that's used for completion checking. + +The tab key requires special handling because it's not entered as a normal character and therefore special checking has to be done to see if the next character is a tab character. Type doesn't handling using the tab key with space-indentation like most editors/IDEs because most places where you'd encounter significant amounts of whitespace in the middle of a line, it's a tab character used to line up text across multiple lines. + +Escape is handled fairly simply. When escape is pressed and the editor is focused, a global `focus` variable is toggled, causing all other input-handling to be disabled, a **`Paused`** label is added/removed in the top right of the top bar, and lastly the `paused` class is toggled on the page, which, when active, gives the editor 50% opacity, giving it a nice effect that clearly indicates the paused state. + +## Cursor Handling +Preventing the cursor movement (something you obviously don't want for a typing practice thing) that's still possible, even in CodeMirror's read only mode, is accomplished simply by adding an event listener on the CodeMirror `mousedown` event and calling `preventDefault` on the event to prevent the editor's default behavior from taking place and calls the `focus` method on the editor instance, focusing it and un-pauses it if it was previously paused. + +## Syntax Highlighting +Syntax highlighting is handled completely using CodeMirror's [modes](http://codemirror.net/mode/index.html), so Type supports* everything that CodeMirror does. By default, Type will try to automatically detect a language mode to use based on the file's extension, falling back to plain-text if a mode can't be found. This is accomplished by searching the (ridiculously large and manually written) [langauges map](https://github.com/shadowfacts/type/blob/master/js/languages.js) that stores A) the JS CodeMirror mode file to load, B) the MIME type to pass to CodeMirror, and C) the extensions for that file-type (based on GitHub [linguis](https://github.com/github/linguist) data). Yes, I spent far too long manually writing that file when I probably could have [automated](https://xkcd.com/1319/) it. The script for the mode is then loaded using `jQuery.getScript`and the code, along with the MIME type, and a couple other things, are passed into `CodeMirror.fromTextArea`. + +\* Technically it does, however only a subset of those languages can actually be used because they seem common enough** to warrant being manually added to the languages map. + +** I say "common" but [Brainfuck](https://github.com/shadowfacts/type/blob/master/js/languages.js#L2) and [FORTRAN](https://github.com/shadowfacts/type/blob/master/js/languages.js#L142) aren't really common, I just added them for shits and giggles. + +## Themes +Themes are handled fairly similarly to syntax highlighting. There's a massive `