on master help sign in
Merge branch 'urbit.org2' of github.com:urbit/urbit.org into urbit.org2
8aef7a8Gavin Atkinson
content/docs/.gitignoredeleted−4
@@ -1,4 +0,0 @@
1−# OS
2−.DS_Store
3−# etc
4−*.swn
content/docs/.gitmodulesdeleted−3
@@ -1,3 +0,0 @@
1−[submodule "docs/hoon/workbook"]
2− path = docs/hoon/workbook
3− url = git@github.com:urbit/hoon-workbook.git
content/docs/LICENSEdeleted−21
@@ -1,21 +0,0 @@
1−MIT License
2−
3−Copyright (c) 2019 Tlon Corporation
4−
5−Permission is hereby granted, free of charge, to any person obtaining a copy
6−of this software and associated documentation files (the "Software"), to deal
7−in the Software without restriction, including without limitation the rights
8−to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9−copies of the Software, and to permit persons to whom the Software is
10−furnished to do so, subject to the following conditions:
11−
12−The above copyright notice and this permission notice shall be included in all
13−copies or substantial portions of the Software.
14−
15−THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16−IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17−FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18−AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19−LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20−OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21−SOFTWARE.
content/docs/README.markdowndeleted−3
@@ -1,3 +0,0 @@
1−# Urbit Docs
2−
3−Urbit documentation as it appears on [urbit.org](https://urbit.org/docs/).
content/docs/anchors.pydeleted−135
@@ -1,135 +0,0 @@
1−#!/usr/bin/env python3
2−
3−import os
4−import sys
5−from pathlib import Path
6−from typing import List, Tuple
7−
8−
9−# inspired by weights.sh
10−
11−# NOTE: This script does not prevent you from shooting yourself in the foot.
12−# If you manually set a bogus value for insert_anchor_links it will not fix it for you
13−"""
14−1. get a list of all .md files
15−2. for each md file
16− a. get front matter
17− b. get value of key `insert_anchor_links`, default to none if not present
18− c. output a tuple of (setting, file)
19−3. accumulate all anchor setting values for all md files
20−4. write tuple to 'anchors' file, prompt to edit, open in preferred editor
21−5. once prompt is answered, read back file
22−6. read edited 'anchors' file, overwrite new setting for each file
23−"""
24−
25−ANCHOR_KEY = "insert_anchor_links = "
26−ANCHOR_FILE = "anchors"
27−
28−def nth_occurrence(arr, target_value, wanted_idx):
29− return [idx for idx, val in enumerate(arr) if val == target_value][wanted_idx - 1]
30−
31−def validate_entry(setting: str, file: str):
32− if not setting in ["left", "right", "none"]:
33− sys.exit(f"Invalid setting '{setting}' provided for file '{file}'")
34−
35−
36−def find_all_mds() -> List[str]:
37− # the `insert_anchor_links` is only applicable on `_index.md` files (i.e. sections)
38− return [str(p) for p in Path('./').rglob('**/.md')]
39−
40−def setting_from_frontmatter(lines: List[str]) -> str:
41− # 'insert_anchor_links = "none"' -> 'none'
42− for line in lines:
43− if line.startswith(ANCHOR_KEY):
44− # 1. 'insert_anchor_links = "none"\n'
45− # 2. ['insert_anchor_links = ' '"none"\n'][1] => '"none"\n'
46− # 3. "none\n"
47− # 4. "none"
48− return line \
49− .split(ANCHOR_KEY)[1] \
50− .replace('\"', '') \
51− .replace('\n', '')
52−
53− return "none" # default setting if key not present
54−
55−def anchors_from_frontmatter(md_files: List[str]) -> List[Tuple[str, str]]:
56− # returns tuple(setting, file)
57− anchor_settings = []
58−
59− for md_file in md_files:
60− with open(md_file, "r") as f:
61− lines = f.readlines()
62− setting = setting_from_frontmatter(lines)
63− anchor_settings.append((setting, md_file))
64−
65− return anchor_settings
66−
67−def write_anchor_file(anchor_settings: List[Tuple[str, str]]):
68− with open(ANCHOR_FILE, "w+") as f:
69− for setting, file in anchor_settings:
70− f.write(f"{setting} {file}\n")
71−
72−
73−def prompt_editor():
74− editor = os.getenv('EDITOR', 'nano')
75− os.system(f"{editor} {ANCHOR_FILE}")
76− input("Edit the anchor settings and press enter...")
77−
78−def read_anchor_file() -> List[Tuple[str, str]]:
79− anchor_settings = []
80−
81− with open(ANCHOR_FILE, "r") as f:
82− for line in f:
83− line = line.replace('\n', '')
84− setting, file = line.split(' ')
85− anchor_settings.append((setting, file))
86−
87− return anchor_settings
88−
89−
90−def write_entry(setting: str, file: str):
91− validate_entry(setting, file)
92−
93−
94− # not optimized
95− with open(file, "r+") as f:
96− lines = f.readlines()
97− # lines = list(filter(bool, text.split('\n'))) # split text by lines,
98− fm_end = nth_occurrence(lines, "+++\n", 2) # get index of end of front matter (signaled by second '+++')
99−
100− # remove existing setting if it exists
101− for line in lines[:fm_end]:
102− if line.startswith(ANCHOR_KEY):
103− lines.remove(line)
104− fm_end -= 1 # correct the index for the removal
105−
106− if setting != "none":
107− # by default, there is no need to specify "none"
108− lines.insert(fm_end, ANCHOR_KEY + f'"{setting}"' + '\n') # insert the setting right before the end of front matter
109− f.seek(0) # go to beginning of file
110− f.writelines(lines) # overwrite with new contents
111− f.truncate() # idk why but necessary for bug-free behviour (otherwise it adds excess newlines and stuff)
112−
113−
114−
115−def anchors_to_frontmatter(anchor_settings: List[Tuple[str, str]]):
116− for setting, file in anchor_settings:
117− write_entry(setting, file)
118−
119−def cleanup():
120− os.remove("anchors")
121−
122−
123−def main():
124− files = find_all_mds()
125− print(f'found {len(files)} .md files')
126− write_anchor_file(anchors_from_frontmatter(files))
127− prompt_editor()
128− anchors_to_frontmatter(read_anchor_file())
129− cleanup()
130− print('done')
131−
132−
133−if __name__ == '__main__':
134− main()
135−
content/docs/weights.shdeleted−23
@@ -1,23 +0,0 @@
1−#!/bin/bash
2−
3−EDITOR=${EDITOR:-atom}
4−
5−# build weights file
6−for file in $(find -type f -name \*.md); do
7− [[ $file == *_index.md ]] && continue
8− echo $(grep -m 1 "weight = " $file | sed 's/^weight = //') $file
9−done | sort -n > weights
10−
11−# edit weights file
12−$EDITOR weights
13−read -p "Edit weights and press enter to continue..."
14−
15−# for each line in weights file, read values weight and file, and update
16−# $weight in $file
17−cat weights | while read weight file; do
18− echo $weight $file
19− sed -i'' "s/^weight = [.0-9]*/weight = $weight/" $file
20−done
21−
22−# remove weights file
23−rm weights
lib/lib.js+118−65
@@ -1,9 +1,9 @@
1−import remark from 'remark'
1+import remark from "remark";
2−import html from 'remark-html'
2+import html from "remark-html";
3−import fs from 'fs'
3+import fs from "fs";
4−import { join } from 'path'
4+import { join, parse } from "path";
5−import matter from 'gray-matter'
5+import matter from "gray-matter";
6−import toml from '@iarna/toml'
6+import toml from "@iarna/toml";
7
7
8// export async function markdownToHtml(markdown) {
8// export async function markdownToHtml(markdown) {
9// const result = await remark().use(html).process(markdown)
9// const result = await remark().use(html).process(markdown)
@@ -11,13 +11,13 @@ import toml from '@iarna/toml'
11// }
11// }
12
12
13const directories = {
13const directories = {
14− blog: join(process.cwd(), 'content/blog'),
14+ blog: join(process.cwd(), "content/blog"),
15− events: join(process.cwd(), 'content/events'),
15+ events: join(process.cwd(), "content/events"),
16− docs: join(process.cwd(), 'content/docs'),
16+ docs: join(process.cwd(), "content/docs"),
17−}
17+};
18
18
19export function getPostSlugs(key) {
19export function getPostSlugs(key) {
20− return fs.readdirSync(directories[key])
20+ return fs.readdirSync(directories[key]);
21}
21}
22
22
23// NB Gavin: This could be much simpler if we converted everything to a more common standard, like YAML
23// NB Gavin: This could be much simpler if we converted everything to a more common standard, like YAML
@@ -25,79 +25,87 @@ const options = {
25 engines: {
25 engines: {
26 toml: toml.parse.bind(toml),
26 toml: toml.parse.bind(toml),
27 },
27 },
28− language: 'toml',
28+ language: "toml",
29− delimiters: '+++'
29+ delimiters: "+++",
30−}
30+};
31
31
32export function getPostBySlug(slug, fields = [], key) {
32export function getPostBySlug(slug, fields = [], key) {
33− const realSlug = slug.replace(/\.md$/, '')
33+ const realSlug = slug.replace(/\.md$/, "");
34− const fullPath = join(directories[key], `${realSlug}.md`)
34+ const fullPath = join(directories[key], `${realSlug}.md`);
35− const fileContents = fs.readFileSync(fullPath, 'utf8')
35+ const fileContents = fs.readFileSync(fullPath, "utf8");
36− const { data, content } = matter(fileContents, options)
36+ const { data, content } = matter(fileContents, options);
37− const items = {}
37+ const items = {};
38−
38+
39 // Ensure only the minimal needed data is exposed
39 // Ensure only the minimal needed data is exposed
40 fields.forEach((field) => {
40 fields.forEach((field) => {
41−
42− if (field === 'slug') {
41+ if (field === "slug") {
43− items[field] = realSlug
42+ items[field] = realSlug;
44− }
43+ }
45−
44+
46− if (field === 'content') {
45+ if (field === "content") {
47− items[field] = content
46+ items[field] = content;
48− }
47+ }
49−
48+
50− if (data[field]) {
49+ if (data[field]) {
51− items[field] = data[field]
50+ items[field] = data[field];
52− }
51+ }
53−
54− })
52+ });
55−
53+
56 // NB Gavin: This could be removed if the TOML engine returned a JSON date (string) instead of a JS Date object. The YAML engine did this parsing by default.
54 // NB Gavin: This could be removed if the TOML engine returned a JSON date (string) instead of a JS Date object. The YAML engine did this parsing by default.
57− items.date = JSON.stringify(items.date).replace(/\"/g, '');
55+ items.date = JSON.stringify(items.date).replace(/\"/g, "");
58−
59
56
60− return items
57+ return items;
61}
58}
62
59
63export function getAllPosts(fields = [], key) {
60export function getAllPosts(fields = [], key) {
64− const slugs = getPostSlugs(key)
61+ const slugs = getPostSlugs(key);
65 const posts = slugs
62 const posts = slugs
66− .map((slug) => getPostBySlug(slug, fields, key))
63+ .map((slug) => getPostBySlug(slug, fields, key))
67− // sort posts by date in descending order
64+ // sort posts by date in descending order
68− .sort((post1, post2) => (post1.date > post2.date ? -1 : 1))
65+ .sort((post1, post2) => (post1.date > post2.date ? -1 : 1));
69−
66+
70− return posts
67+ return posts;
71}
68}
72
69
73export function getNextPost(slug, fields = [], key) {
70export function getNextPost(slug, fields = [], key) {
74− let resultPost = null
71+ let resultPost = null;
75−
72+
76− getAllPosts(fields, key)
77− .forEach((post, index, array) => {
73+ getAllPosts(fields, key).forEach((post, index, array) => {
78− if (post.slug === slug) {
74+ if (post.slug === slug) {
79− resultPost = array[index - 1]
75+ resultPost = array[index - 1];
80− }
76+ }
81− })
77+ });
82− return resultPost
78+ return resultPost;
83}
79}
84
80
85export function getPreviousPost(slug, fields = [], key) {
81export function getPreviousPost(slug, fields = [], key) {
86− let resultPost = null
82+ let resultPost = null;
87−
83+
88− getAllPosts(fields, key)
89− .forEach((post, index, array) => {
84+ getAllPosts(fields, key).forEach((post, index, array) => {
90− if (post.slug === slug) {
85+ if (post.slug === slug) {
91− resultPost = array[index + 1]
86+ resultPost = array[index + 1];
92− }
87+ }
93− })
88+ });
94− return resultPost
89+ return resultPost;
95}
90}
96
91
97export function formatDate(d) {
92export function formatDate(d) {
98 // const wk = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
93 // const wk = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
99− const mn =["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
94+ const mn = [
95+ "January",
96+ "February",
97+ "March",
98+ "April",
99+ "May",
100+ "June",
101+ "July",
102+ "August",
103+ "September",
104+ "October",
105+ "November",
106+ "December",
107+ ];
100− return mn[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear()
108+ return mn[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear();
101}
109}
102
110
103// export function capitalize(str) {
111// export function capitalize(str) {
@@ -113,4 +121,49 @@ export function formatDate(d) {
113// key: crumb,
121// key: crumb,
114// path: crumbs.slice(0, index+2).join('/'),
122// path: crumbs.slice(0, index+2).join('/'),
115// }))
123// }))
116−// }
124+// }
125+
126+export const getAllFiles = function (dirPath, arrayOfFiles) {
127+ let files = fs.readdirSync(dirPath);
128+
129+ arrayOfFiles = arrayOfFiles || [];
130+
131+ files.forEach(function (file) {
132+ if (fs.statSync(dirPath + "/" + file).isDirectory()) {
133+ arrayOfFiles = getAllFiles(dirPath + "/" + file, arrayOfFiles);
134+ } else {
135+ arrayOfFiles.push(join(__dirname, dirPath, "/", file));
136+ }
137+ });
138+
139+ return arrayOfFiles;
140+};
141+
142+export function getLinksinSubdirs(path) {
143+ const allFiles = getAllFiles(path);
144+ const fileTree = {};
145+ allFiles.forEach((e) => {
146+ const parsed = parse(e.substr(e.indexOf("content/") + 7));
147+
148+ if (!(parsed.dir in fileTree)) {
149+ const fullPathofDir = join(
150+ process.cwd(),
151+ "/content",
152+ parsed.dir,
153+ "_index.md"
154+ );
155+ const dirContents = fs.readFileSync(fullPathofDir, "utf8");
156+ const { data: dirData } = matter(dirContents, options);
157+ fileTree[parsed.dir] = { name: dirData.title, pages: [] };
158+ }
159+
160+ const fileContents = fs.readFileSync(e, "utf8");
161+ const { data } = matter(fileContents, options);
162+ fileTree[parsed.dir].pages.push({
163+ name: data.title,
164+ base: parsed.base,
165+ slug: parsed.name,
166+ });
167+ });
168+ return fileTree;
169+}