on master help sign in
urbit.org/lib/lib.js
251 lines 8.1 kB history raw
1import fs from "fs";
2import { join, parse } from "path";
3import matter from "gray-matter";
4import toml from "@iarna/toml";
5import { DateTime } from "luxon";
6import { Markdown, getPostBySlug, getPostSlugs } from "@urbit/foundation-design-system";
8// NB Gavin: This could be much simpler if we converted everything to a more common standard, like YAML
9const options = {
10 engines: {
11 toml: toml.parse.bind(toml),
12 },
13 language: "toml",
14 delimiters: "+++",
15};
17const directories = (dir) => {
18 switch (dir) {
19 case "blog":
20 return join(process.cwd(), "content/blog");
21 case "events":
22 return join(process.cwd(), "content/events");
23 case "docs":
24 return join(process.cwd(), "content/docs");
25 case "grants":
26 return join(process.cwd(), "content/grants");
27 case "media":
28 return join(process.cwd(), "content/media");
29 case "updates":
30 return join(process.cwd(), "content/updates");
31 case "/":
32 return join(process.cwd(), "content");
33 default:
34 return join(process.cwd(), `content/${dir}`);
35 }
36};
38export function getAllPosts(fields = [], key, sort = "") {
39 const slugs = getPostSlugs(key);
40 const posts = slugs
41 .map((slug) => getPostBySlug(slug, fields, key))
42 // sort posts by date in descending order
43 .sort((post1, post2) => {
44 if (sort === "date") {
45 return DateTime.fromISO(post1.date) > DateTime.fromISO(post2.date)
46 ? -1
47 : 1;
48 } else if (sort === "weight") {
49 return post1.weight > post2.weight ? -1 : 1;
50 }
51 });
52 return posts;
55export function getAllEvents(fields = [], key) {
56 const slugs = getPostSlugs(key);
57 const posts = slugs
58 .map((slug) => getPostBySlug(slug, fields, key))
59 // sort posts by date in descending order
60 .sort((post1, post2) =>
61 DateTime.fromISO(post1.starts) > DateTime.fromISO(post2.starts) ? -1 : 1
62 );
63 return posts;
66function getAllPaths(dirPath, arrayOfFiles) {
67 let files = fs.readdirSync(dirPath);
69 arrayOfFiles = arrayOfFiles || [];
71 files.forEach(function (file) {
72 if (fs.statSync(dirPath + "/" + file).isDirectory()) {
73 arrayOfFiles = getAllPaths(dirPath + "/" + file, arrayOfFiles);
74 } else {
75 arrayOfFiles.push(join(dirPath, "/", file));
76 }
77 });
79 return arrayOfFiles;
82export function getDocsPaths() {
83 return getAllPaths(directories("docs"), []);
86export function formatDateWithTime(d) {
87 const date = DateTime.fromISO(d, {
88 setZone: true,
89 }).toLocaleString(DateTime.DATETIME_FULL);
90 return date;
93// Formats a DateTime object to something like "July 15, 2021, 5:00 PM"
94export function formatDateAndTime(dateTimeObject) {
95 return dateTimeObject.toLocaleString(DateTime.DATETIME_HUGE);
98// Returns an array of unique grants categories
99export function getGrantsCategories() {
100 const years = fs.readdirSync(directories("grants"), { withFileTypes: true }).filter((f) => f.isDirectory());
101 const uniqueCategories = Array.from(new Set(years.map((yearDir) => {
102 return fs.readdirSync(join(directories("grants"), yearDir.name), { withFileTypes: true })
103 .filter((path) => parse(path.name).ext === ".md")
104 .map((path) => matter(fs.readFileSync(join(directories("grants"), yearDir.name, path.name)), options))
105 .flat()
106 .reduce((acc, metadata) => {
107 return [...acc, ...metadata.data.taxonomies.grant_category]
108 }, [])
109 }).flat()));
110 return uniqueCategories;
113// Returns an array of unique grants types
115export function getGrantsTypes() {
116 const years = fs.readdirSync(directories("grants"), { withFileTypes: true }).filter((f) => f.isDirectory());
117 const uniqueTypes = Array.from(new Set(years.map((yearDir) => {
118 return fs.readdirSync(join(directories("grants"), yearDir.name), { withFileTypes: true })
119 .filter((path) => parse(path.name).ext === ".md")
120 .map((path) => matter(fs.readFileSync(join(directories("grants"), yearDir.name, path.name)), options))
121 .flat()
122 .reduce((acc, metadata) => {
123 return [...acc, ...metadata.data.taxonomies.grant_type]
124 }, [])
125 }).flat()));
126 return uniqueTypes;
129// Grab which directory the grant post is in
131export function getGrantYear(slug) {
132 const basePath = join(process.cwd(), 'content/grants')
133 const years = fs.readdirSync(basePath, { 'withFileTypes': true }).filter((f) => f.isDirectory());
134 const dir = years.find((year) => {
135 const yearDir = fs.readdirSync(join(basePath, year.name), { 'withFileTypes': true });
136 return yearDir.some((file) => file.name === `${slug}.md`)
137 });
138 return dir;
141export function getGrantsByShortcode(shortcode) {
142 const basePath = join(process.cwd(), 'content/grants')
143 const years = fs.readdirSync(basePath, { 'withFileTypes': true }).filter((f) => f.isDirectory());
144 let found = [];
145 // Get matching years by any matching grant.
146 years.forEach((year) => {
147 const yearDir = fs.readdirSync(join(basePath, year.name), { 'withFileTypes': true });
148 return yearDir.forEach((file) => {
149 const { data } = matter(fs.readFileSync(join(basePath, year.name, file.name)), options);
150 if (data?.extra?.deliverable === shortcode.join("/")) {
151 found.push({
152 ...data, ...{ slug: file.name.replace('.md', '') }
153 }
154 )
155 }
156 })
157 });
158 return found
161// Groups arrays into an array of arrays with 2 members
162export const pair = (arr) =>
163 arr.reduce(function (rows, key, index) {
164 return (
165 (index % 2 == 0 ? rows.push([key]) : rows[rows.length - 1].push(key)) &&
166 rows
167 );
168 }, []);
170// Match ecosystem report to posts to inject metadata
171export const matchEcosystemPost = (ecosystem) => {
172 const articles = getAllPosts(
173 ["title", "image", "date", "podcast", "slug"],
174 "articles",
175 "date"
176 );
178 const marketplaces = getAllPosts(["title", "image", "slug"], "marketplaces");
179 const podcasts = getAllPosts(
180 ["title", "image", "date", "podcast", "slug"],
181 "podcasts",
182 "date"
183 );
184 const organizations = getAllPosts(
185 ["title", "image", "slug"],
186 "organizations"
187 );
188 const applications = fs
189 .readdirSync(join(process.cwd(), "content/applications"), {
190 withFileTypes: true,
191 })
192 .filter((f) => f.isDirectory())
193 .map((dir) =>
194 getAllPosts(
195 ["title", "bgColor", "image", "slug", "description"],
196 `applications/${dir.name}`
197 )
198 .map((e) => ({ ...e, ship: dir.name }))
199 .flat()
200 )
201 .flat()
202 .sort((a, b) => {
203 const nameA = a.title.toLowerCase();
204 const nameB = b.title.toLowerCase();
205 return nameA < nameB ? -1 : 1;
206 });
208 const groups = fs
209 .readdirSync(join(process.cwd(), "content/groups"), {
210 withFileTypes: true,
211 })
212 .filter((f) => f.isDirectory())
213 .map((dir) =>
214 getAllPosts(
215 ["title", "tile", "slug", "description"],
216 `groups/${dir.name}`
217 )
218 .map((e) => ({ ...e, ship: dir.name }))
219 .flat()
220 )
221 .flat()
222 .sort((a, b) => {
223 const nameA = a.title.toLowerCase();
224 const nameB = b.title.toLowerCase();
225 return nameA < nameB ? -1 : 1;
226 });
228 ["featured-1", "featured-2", "featured-3"].forEach((feat) => {
229 if (ecosystem?.[feat]) {
230 const matchedPost = [
231 ...articles.map((e) => ({ ...e, type: "Article" })),
232 ...applications.map((e) => ({ ...e, type: "Application" })),
233 ...organizations.map((e) => ({ ...e, type: "Organization" })),
234 ...podcasts.map((e) => ({ ...e, type: "Podcast" })),
235 ...marketplaces.map((e) => ({ ...e, type: "Marketplace" })),
236 ...groups.map((e) => ({ ...e, type: "Group" }))
237 ].filter((e) => e.title === ecosystem[feat].title)?.[0];
238 ecosystem[feat].image =
239 ecosystem[feat].image || matchedPost?.image || null;
240 ecosystem[feat].type = matchedPost?.type || "Podcast";
241 ecosystem[feat].matchedPost = matchedPost || null;
242 ecosystem[feat].content = JSON.stringify(
243 Markdown.parse({
244 post: { content: ecosystem[feat].content },
245 })
246 );
247 }
248 });
250 return ecosystem;