vowel 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/.prettierrc +8 -0
  2. package/.vscode/settings.json +3 -0
  3. package/README.md +123 -27
  4. package/bin.js +30 -0
  5. package/content/.cache.json +627 -0
  6. package/content/.obsidian/app.json +3 -0
  7. package/content/.obsidian/appearance.json +3 -0
  8. package/content/.obsidian/core-plugins-migration.json +30 -0
  9. package/content/.obsidian/core-plugins.json +20 -0
  10. package/content/.obsidian/workspace.json +168 -0
  11. package/content/about.md +3 -0
  12. package/content/assets/open-props.css +1630 -0
  13. package/content/assets/styles-2.css +128 -0
  14. package/content/assets/styles-3.css +275 -0
  15. package/content/docs/file-structure.md +31 -0
  16. package/content/docs/folder-settings.md +22 -0
  17. package/content/docs/home.md +8 -0
  18. package/content/docs/images.md +10 -0
  19. package/content/docs/pages.md +64 -0
  20. package/content/docs/quickstart.md +52 -0
  21. package/content/docs/run-build-deploy.md +20 -0
  22. package/content/docs/settings.md +4 -0
  23. package/content/docs/styling.md +10 -0
  24. package/content/docs/taxonomies.md +37 -0
  25. package/content/home.md +73 -0
  26. package/content/roadmap.md +81 -0
  27. package/content/settings.md +12 -0
  28. package/content/vercel.json +5 -0
  29. package/jsconfig.json +18 -0
  30. package/package.json +37 -19
  31. package/server.js +80 -0
  32. package/src/app.d.ts +12 -0
  33. package/src/app.html +12 -0
  34. package/src/lib/components/Breadcrumbs.svelte +19 -0
  35. package/src/lib/components/DefaultStyles.svelte +126 -0
  36. package/src/lib/components/FrontMatterTaxonomy.svelte +48 -0
  37. package/src/lib/components/Frontmatter.svelte +54 -0
  38. package/src/lib/components/FrontmatterProperty.svelte +72 -0
  39. package/src/lib/components/Markdown/Image.svelte +48 -0
  40. package/src/lib/components/Markdown/Link.svelte +10 -0
  41. package/src/lib/components/Markdown/LinkPreview.svelte +39 -0
  42. package/src/lib/components/Markdown/Text.svelte +6 -0
  43. package/src/lib/components/Markdown/index.svelte +84 -0
  44. package/src/lib/components/Markdown/validators.js +29 -0
  45. package/src/lib/components/Nav.svelte +39 -0
  46. package/src/lib/components/Page.svelte +59 -0
  47. package/src/lib/components/Sitemap.svelte +38 -0
  48. package/src/lib/components/index.js +9 -0
  49. package/src/lib/index.js +1 -0
  50. package/src/lib/utilities/buildURL.js +18 -0
  51. package/src/lib/utilities/checkFileExists.js +16 -0
  52. package/src/lib/utilities/createFolderClass.js +4 -0
  53. package/src/lib/utilities/createPageClass.js +4 -0
  54. package/src/lib/utilities/getFileLabel.js +18 -0
  55. package/src/lib/utilities/getFolder.js +16 -0
  56. package/src/lib/utilities/getFolderLabel.js +8 -0
  57. package/src/lib/utilities/getPage.js +24 -0
  58. package/src/lib/utilities/getPagesByFolder.js +93 -0
  59. package/src/lib/utilities/index.js +20 -0
  60. package/src/lib/utilities/isActiveLink.js +12 -0
  61. package/src/lib/utilities/isObject.js +8 -0
  62. package/src/lib/utilities/loadCache.js +28 -0
  63. package/src/lib/utilities/mutateMarkdownAST.js +59 -0
  64. package/src/lib/utilities/mutateMarkdownFrontmatter.js +115 -0
  65. package/src/lib/utilities/parseDate.js +43 -0
  66. package/src/lib/utilities/processMarkdownFiles.js +212 -0
  67. package/src/lib/utilities/readMarkdownFile.js +134 -0
  68. package/src/lib/utilities/regexPatterns.js +12 -0
  69. package/src/lib/utilities/resolveHomeDirPath.js +5 -0
  70. package/src/lib/utilities/writeCache.js +14 -0
  71. package/src/routes/[...path]/+layout.server.js +71 -0
  72. package/src/routes/[...path]/+page.server.js +22 -0
  73. package/src/routes/[...path]/+page.svelte +125 -0
  74. package/src/routes/feed.xml/+server.js +120 -0
  75. package/src/routes/robots.txt/+server.js +54 -0
  76. package/src/routes/sitemap.xml/+server.js +68 -0
  77. package/static/favicon.png +0 -0
  78. package/static/styles.css +0 -0
  79. package/svelte.config.js +32 -0
  80. package/vercel.json +5 -0
  81. package/vite.config.js +72 -0
  82. package/index.js +0 -28
@@ -0,0 +1,59 @@
1
+ import urlMetadata from 'url-metadata';
2
+
3
+ export default async function mutateMarkdownAST(ast, cache) {
4
+ const promises = ast.map(async (node) => {
5
+ // TODO: Improve this URL regex
6
+ if (node.type === 'paragraph') {
7
+ // URLs
8
+ if (
9
+ node.children?.length === 1 &&
10
+ node.children[0]?.value?.match(/^https?:\/\/\S+$/) &&
11
+ !node.children[0]?.children
12
+ ) {
13
+ node.type = 'url';
14
+ const url = node.children[0].value;
15
+ if (!cache[node.value]) {
16
+ try {
17
+ const urlObject = new URL(url);
18
+ const response = await urlMetadata(urlObject.href, {
19
+ includeResponseBody: false,
20
+ ensureSecureImageRequest: true
21
+ });
22
+
23
+ const metadata = {
24
+ image: response['og:image'],
25
+ ogURL: response['og:url'],
26
+ canonicalURL: response.canonical,
27
+ title: response.title,
28
+ ogTitle: response['og:title'],
29
+ author: response.author,
30
+ description: response.description
31
+ };
32
+
33
+ cache[url] = metadata;
34
+ node.metadata = metadata;
35
+ node.value = url;
36
+
37
+ delete node.children;
38
+ } catch (error) {
39
+ node.value = url;
40
+ console.log(`Error on URL in content: ${url}`);
41
+ }
42
+ } else {
43
+ node.metadata = cache[node.value];
44
+ }
45
+ } else if (node.children[0].type === 'image') {
46
+ node.type = 'figure';
47
+ if (node?.children[1]?.type === 'text') {
48
+ node.children[1].type = 'figcaption';
49
+ }
50
+ }
51
+ }
52
+
53
+ if (node.children) {
54
+ await mutateMarkdownAST(node.children, cache);
55
+ }
56
+ });
57
+
58
+ await Promise.all(promises);
59
+ }
@@ -0,0 +1,115 @@
1
+ import urlMetadata from 'url-metadata';
2
+ import { regexPatterns, isObject, parseDate } from '.';
3
+
4
+ /**
5
+ * Description
6
+ * @param {any} value - Any frontmatter value
7
+ * @returns {any}
8
+ */
9
+ function imputeType(value) {
10
+ if (value === null || value === undefined || Number.isNaN(value)) return 'nullish';
11
+ if (typeof value === 'boolean') return 'boolean';
12
+ if (typeof value === 'number') return 'number';
13
+ if (parseDate(value)) return 'date';
14
+ if (isObject(value)) return 'object';
15
+ if (Array.isArray(value)) return 'array';
16
+ if (typeof value === 'string') {
17
+ if (value.match(regexPatterns.img)) return 'image';
18
+ if (value.match(regexPatterns.pdf)) return 'pdf';
19
+ if (value.match(regexPatterns.url)) return 'url';
20
+ if (value.match(regexPatterns.path)) return 'path';
21
+ return 'string';
22
+ }
23
+ console.error('Unknown frontmatter data type: ' + value);
24
+ return 'other';
25
+ }
26
+
27
+ export default async function mutateMarkdownFrontmatter(frontmatter, cache) {
28
+ const keys = Object.keys(frontmatter);
29
+
30
+ const promises = keys.map(async (key) => {
31
+ const input = frontmatter[key];
32
+
33
+ const type = imputeType(input);
34
+
35
+ switch (type) {
36
+ case 'object': {
37
+ await mutateMarkdownFrontmatter(input, cache);
38
+ frontmatter[key] = {
39
+ type: 'object',
40
+ output: input
41
+ };
42
+ break;
43
+ }
44
+ case 'array': {
45
+ await mutateMarkdownFrontmatter(input, cache);
46
+ frontmatter[key] = {
47
+ type: 'array',
48
+ output: input
49
+ };
50
+ break;
51
+ }
52
+ case 'date': {
53
+ frontmatter[key] = {
54
+ type: 'date',
55
+ output: parseDate(input),
56
+ input
57
+ };
58
+ break;
59
+ }
60
+ case 'image': {
61
+ frontmatter[key] = {
62
+ type: 'image',
63
+ output: input,
64
+ input
65
+ };
66
+ break;
67
+ }
68
+ case 'pdf': {
69
+ frontmatter[key] = {
70
+ type: 'pdf',
71
+ output: input,
72
+ input
73
+ };
74
+ break;
75
+ }
76
+ case 'url': {
77
+ if (!cache[input]) {
78
+ try {
79
+ const url = new URL(input);
80
+ const metadata = await urlMetadata(url.href, {
81
+ includeResponseBody: false,
82
+ ensureSecureImageRequest: true
83
+ });
84
+ cache[input] = metadata;
85
+
86
+ frontmatter[key] = {
87
+ type: 'url',
88
+ output: metadata,
89
+ input
90
+ };
91
+ } catch (e) {
92
+ console.error(`Error on URL in frontmatter: ${frontmatter[key]}`);
93
+ frontmatter[key] = {
94
+ type: 'string',
95
+ output: input,
96
+ input
97
+ };
98
+ }
99
+ } else {
100
+ frontmatter[key] = {
101
+ type: 'url',
102
+ output: cache[input],
103
+ input
104
+ };
105
+ }
106
+ break;
107
+ }
108
+ case 'nullish': {
109
+ frontmatter[key] = undefined;
110
+ }
111
+ }
112
+ });
113
+
114
+ await Promise.all(promises);
115
+ }
@@ -0,0 +1,43 @@
1
+ import dates from 'any-date-parser';
2
+ import ago from 'any-date-parser/src/formats/ago/ago.js';
3
+ import chinese from 'any-date-parser/src/formats/chinese/chinese.js';
4
+ import dayMonth from 'any-date-parser/src/formats/dayMonth/dayMonth.js';
5
+ import dayMonthname from 'any-date-parser/src/formats/dayMonthname/dayMonthname.js';
6
+ import monthDay from 'any-date-parser/src/formats/monthDay/monthDay.js';
7
+ import monthnameDay from 'any-date-parser/src/formats/monthnameDay/monthnameDay.js';
8
+ import today from 'any-date-parser/src/formats/today/today.js';
9
+
10
+ dates.removeFormat(ago);
11
+ dates.removeFormat(chinese);
12
+ dates.removeFormat(dayMonth);
13
+ dates.removeFormat(dayMonthname);
14
+ dates.removeFormat(monthDay);
15
+ dates.removeFormat(monthnameDay);
16
+ dates.removeFormat(today);
17
+
18
+ /*
19
+ Left in the following formats:
20
+
21
+ microsoftJson
22
+ dayMonthYear
23
+ dayMonthnameYear
24
+ monthDayYear
25
+ monthnameDayYear
26
+ twitter
27
+ yearMonthDay
28
+ atSeconds
29
+ time12Hours
30
+ time24Hours
31
+ */
32
+
33
+ export default function parseDate(maybeDate) {
34
+ if (!maybeDate) return false;
35
+ if (maybeDate instanceof Date) return maybeDate;
36
+ if (typeof maybeDate === 'number') return false;
37
+ if (typeof maybeDate === 'string' && maybeDate.match(/^\d+$/)) return false;
38
+ const parsedDate = dates.attempt(maybeDate);
39
+ if (parsedDate.invalid) return false;
40
+ const { month, day, year } = parsedDate;
41
+ const date = new Date(`${year}-${month}-${day}`);
42
+ return date;
43
+ }
@@ -0,0 +1,212 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ // @ts-ignore
4
+ import { buildURL, checkFileExists, readMarkdownFile } from '.';
5
+
6
+ function DefaultFile(url) {
7
+ return {
8
+ ast: [
9
+ {
10
+ type: 'paragraph',
11
+ children: [
12
+ {
13
+ type: 'h1',
14
+ value: 'hey there',
15
+ // value: url + '?count=10',
16
+ position: {
17
+ start: {
18
+ line: 5,
19
+ column: 1,
20
+ offset: 25
21
+ },
22
+ end: {
23
+ line: 5,
24
+ column: 8,
25
+ offset: 32
26
+ }
27
+ }
28
+ }
29
+ ],
30
+ position: {
31
+ start: {
32
+ line: 5,
33
+ column: 1,
34
+ offset: 25
35
+ },
36
+ end: {
37
+ line: 5,
38
+ column: 8,
39
+ offset: 32
40
+ }
41
+ }
42
+ }
43
+ ],
44
+ url
45
+ };
46
+ }
47
+
48
+ function DefaultDirectory(path) {
49
+ return {
50
+ $: DefaultFile(path),
51
+ _: DefaultFile(path)
52
+ };
53
+ }
54
+
55
+ /** @typedef {Object} AdditionalFileProperties
56
+ * @property {string} [url] - URL to the page for the file.
57
+ * @property {number} [date] - Document date.
58
+ */
59
+
60
+ /** @typedef {import("./readMarkdownFile").ParsedMarkdown & AdditionalFileProperties} MarkdownFile */
61
+
62
+ /**
63
+ * @typedef {Object} Subdirectory
64
+ * @description A subdirectory of markdown files
65
+ * @property {MarkdownFile} $ A home file
66
+ * @property {MarkdownFile} _ A settingsfile
67
+ * @property {Object.<string, Object>} [additionalProps] Files or child directories.
68
+ */
69
+
70
+ /**
71
+ * @typedef {Object.<string, MarkdownFile | Subdirectory>} Directory
72
+ * @description A directory of markdown files
73
+ * @property {MarkdownFile} $ A home file.
74
+ * @property {MarkdownFile} _ A settings file.
75
+ * @property {Object.<string, MarkdownFile | Subdirectory>} [additionalProps] Files or child directories.
76
+ */
77
+
78
+ /**
79
+ * Recursively read a directory of markdown files and return an object of the files.
80
+ * @param {string} directoryPath - The directory to read.
81
+ * @param {string} parents - A string representing the parent directories.
82
+ * @param {import('./loadCache').Cache} cache
83
+ * @returns {Promise<Directory>}
84
+ */
85
+
86
+ /**
87
+ * Recursively read a directory of markdown files and return an object of the files.
88
+ * @param {string} folderPath - The directory to read.
89
+ * @param {string} parents - A string representing the parent directories.
90
+ * @param {import('./loadCache').Cache} cache
91
+ * @returns {Promise<Directory>}
92
+ */
93
+ async function readFolder(folderPath, parents, cache) {
94
+ function filterFiles(file) {
95
+ if (file.name.startsWith('.')) return false;
96
+ if (file.name.startsWith('README')) return false;
97
+ if (file.isFile() && !file.name.endsWith('.md')) return false;
98
+ if (file.name === 'node_modules') return false;
99
+ if (file.name === 'assets') return false;
100
+ if (file.name === 'templates') return false;
101
+ return true;
102
+ }
103
+
104
+ let files = (await fs.readdir(folderPath, { withFileTypes: true })).filter(filterFiles);
105
+
106
+ const promises = files.map(async (file) => await readFile(file, parents, cache, folderPath));
107
+
108
+ const folder = (await Promise.all(promises)).reduce((acc, obj) => ({ ...acc, ...obj }), {});
109
+
110
+ if (typeof folder === 'object' && !folder?.$) {
111
+ folder.$ = DefaultFile(parents || '/');
112
+ folder.$.imputedProperties = {
113
+ fileName: parents.split('/').at(-1) || 'Home'
114
+ };
115
+ }
116
+
117
+ if (typeof folder === 'object' && !folder?._) {
118
+ folder._ = DefaultFile(parents);
119
+ }
120
+
121
+ return folder;
122
+ }
123
+
124
+ async function readFile(file, parents, cache, folderPath) {
125
+ const route = path.parse(file.name).name;
126
+ const url = buildURL(parents, route);
127
+ const filePath = path.join(folderPath, file.name);
128
+
129
+ const extension = path.extname(file.name);
130
+
131
+ if (file.isFile() && extension === '.md') {
132
+ const startLoad = performance.now();
133
+ const shortPath = parents + '/' + file.name;
134
+
135
+ const { ast, frontmatter, imputedProperties } = await readMarkdownFile(filePath, cache);
136
+ const loadTime = (performance.now() - startLoad).toFixed(2);
137
+ console.log(`📄 ${shortPath} (${loadTime}ms)`);
138
+ if (frontmatter.published == false) return;
139
+ if (file.name === 'home.md' || file.name === 'settings.md') {
140
+ const key = file.name === 'home.md' ? '$' : '_';
141
+ return {
142
+ [key]: {
143
+ ...frontmatter,
144
+ imputedProperties,
145
+ ast,
146
+ url
147
+ }
148
+ };
149
+ }
150
+ return {
151
+ [route]: {
152
+ $: {
153
+ ...frontmatter,
154
+ imputedProperties,
155
+ ast,
156
+ url
157
+ }
158
+ }
159
+ };
160
+ }
161
+
162
+ /** @type Directory */
163
+ if (file.isDirectory()) {
164
+ const settingsPath = path.join(folderPath, file.name, 'settings.md');
165
+ const settingsFileExists = await checkFileExists(settingsPath);
166
+
167
+ /** @type {MarkdownFile} */
168
+ const _ = settingsFileExists ? await readMarkdownFile(settingsPath, cache) : DefaultFile(url);
169
+
170
+ const shortPath = parents + '/' + file.name;
171
+
172
+ const startLoad = performance.now();
173
+ const folder = await readFolder(path.join(folderPath, file.name), url, cache);
174
+
175
+ const loadTime = (performance.now() - startLoad).toFixed(2);
176
+ console.log(`📁 ${shortPath} (${loadTime}ms)`);
177
+
178
+ if (!folder.hasOwnProperty('$')) {
179
+ const $ = DefaultFile(url);
180
+ Object.assign(folder, { $ });
181
+ }
182
+ if (!folder.hasOwnProperty('_')) {
183
+ const _ = DefaultFile(url);
184
+ Object.assign(folder, { _ });
185
+ }
186
+
187
+ return {
188
+ [file.name]: folder
189
+ };
190
+ }
191
+ }
192
+
193
+ /**
194
+ * @typedef {Object} ProcessedFiles
195
+ * @description The return value of the processing function.
196
+ * @property {Directory} folder - The root directory.
197
+ * @property {Cache} finalCache - The application cache.
198
+ */
199
+
200
+ /**
201
+ * Recursively reads the markdown files in a directory and returns a representative object.
202
+ * @param {import('./loadCache').Cache} cache
203
+ * @returns {Promise<ProcessedFiles>}
204
+ */
205
+ export default async function processMarkdownFiles(cache) {
206
+ /** @type {Array<string>} */
207
+ // @ts-ignore
208
+ const [homeDir] = $home;
209
+ const folder = await readFolder(homeDir, '', cache);
210
+
211
+ return { folder, finalCache: cache };
212
+ }
@@ -0,0 +1,134 @@
1
+ import fs from 'fs/promises';
2
+ import { remark } from 'remark';
3
+ import remarkFrontmatter from 'remark-frontmatter';
4
+ import remarkHTML from 'remark-html';
5
+ import yaml from 'js-yaml';
6
+ import { toString } from 'mdast-util-to-string';
7
+ import path from 'path';
8
+
9
+ import { mutateMarkdownAST, mutateMarkdownFrontmatter } from '.';
10
+
11
+ /**
12
+ * @typedef {Object} FrontmatterNode
13
+ * @property {"yaml"} type - The type is yaml.
14
+ * @property {string} value - The value of the node.
15
+ * @property {Object} [position] - The position of the node.
16
+ */
17
+
18
+ /**
19
+ * Parse a frontmatter node to a JS object.
20
+ * @param {FrontmatterNode | undefined} frontmatterNode - A markdown node of type "yaml".
21
+ * @returns {Object} A JS object containing the frontmatter
22
+ */
23
+ function parseFrontmatter(frontmatterNode) {
24
+ if (!frontmatterNode) return {};
25
+ const frontmatter = yaml.load(frontmatterNode.value);
26
+ if (frontmatter && typeof frontmatter === 'object') {
27
+ return frontmatter;
28
+ }
29
+ return {};
30
+ }
31
+
32
+ function deduceDescriptionFromAST(ast) {
33
+ for (let i = 0; i < ast?.children?.length; i++) {
34
+ if (ast?.children?.[i]?.type === 'paragraph') {
35
+ const firstParagraph = toString(ast[i]);
36
+ if (firstParagraph.length > 150) {
37
+ return firstParagraph.slice(0, 150);
38
+ }
39
+ return firstParagraph;
40
+ }
41
+ }
42
+ return null;
43
+ }
44
+
45
+ function demoteHeadings(ast, start) {
46
+ for (let i = start; i < ast.length; i++) {
47
+ const node = ast[i];
48
+ if (node.type === 'heading') {
49
+ node.depth = node.depth + 1;
50
+ }
51
+ }
52
+ }
53
+
54
+ function checkHeadings(ast, start = 1) {
55
+ for (let i = start; i < ast.length; i++) {
56
+ if (ast[i]?.depth === 1) {
57
+ return true;
58
+ }
59
+ }
60
+ return false;
61
+ }
62
+
63
+ function extractTitleFromHeading(node) {
64
+ return node?.children?.[0].value;
65
+ }
66
+
67
+ function imputeTitleFromAST(ast) {
68
+ if (ast?.[0]?.depth === 1) {
69
+ return extractTitleFromHeading(ast[0]);
70
+ }
71
+
72
+ return null;
73
+ }
74
+
75
+ /**
76
+ * @typedef {Object} ParsedMarkdown
77
+ * @property {Object.<string, *>} [frontmatter] - Metadata for the document.
78
+ * @property {Array<Object>} ast - The content of the document.
79
+ * @property {Object} imputedProperties - Metadata imputed from content.
80
+ * @property {string} [url] - URL for the page.
81
+ * @description An object containing the parsed frontmatter and filtered AST.
82
+ */
83
+
84
+ /**
85
+ * Reads a Markdown file and parses its content into frontmatter and abstract syntax tree (AST).
86
+ * It also filters out YAML frontmatter nodes from the AST.
87
+ *
88
+ * @async
89
+ * @function
90
+ * @param {string} filePath - The path to the Markdown file.
91
+ * @param {Object} cache - An optional cache object that can be passed to mutate the AST.
92
+ * @returns {Promise<ParsedMarkdown>}
93
+ * @throws {Error} Will throw an error if the file cannot be read or parsed.
94
+ */
95
+ export default async function readMarkdownFile(filePath, cache) {
96
+ const fileName = path.basename(filePath.slice(0, -3));
97
+
98
+ const fileContent = await fs.readFile(filePath, 'utf8');
99
+ const markdownParser = remark().use(remarkFrontmatter, ['yaml']).use(remarkHTML);
100
+
101
+ const parsedMarkdown = markdownParser.parse(fileContent);
102
+ const frontmatterNode = parsedMarkdown.children.find((node) => node.type === 'yaml');
103
+ const frontmatter = frontmatterNode?.type === 'yaml' ? parseFrontmatter(frontmatterNode) : {};
104
+ const ast = remark().use(remarkFrontmatter).parse(fileContent);
105
+ const filteredAST = ast.children.filter((child) => child.type !== 'yaml');
106
+
107
+ const imputedTitle = imputeTitleFromAST(filteredAST);
108
+
109
+ if (imputedTitle) {
110
+ filteredAST.shift();
111
+ }
112
+
113
+ if (imputedTitle && checkHeadings(filteredAST, 1)) {
114
+ demoteHeadings(filteredAST, 1);
115
+ }
116
+
117
+ if (!frontmatter.title && imputedTitle) frontmatter.title = imputedTitle;
118
+
119
+ const imputedProperties = {
120
+ description: deduceDescriptionFromAST(ast),
121
+ title: imputedTitle,
122
+ fileName
123
+ };
124
+
125
+ const promises = [mutateMarkdownAST(ast.children, cache)];
126
+
127
+ // Don't mutate frontmatter on the settings pages
128
+ if (!filePath.endsWith('settings.md'))
129
+ promises.push(mutateMarkdownFrontmatter(frontmatter, cache));
130
+
131
+ await Promise.all(promises);
132
+
133
+ return { frontmatter, ast: filteredAST, imputedProperties };
134
+ }
@@ -0,0 +1,12 @@
1
+ const img = /^https?:\/\/\S+(avif|gif|heif|jpeg|jpg|png|tiff|webp)(\?\S+)?/;
2
+ const pdf = /^https?:\/\/\S+(pdf)(\?\S+)?/;
3
+ const url = /^https?:\/\/\S+$/;
4
+ const path = /^\/[\/\S]+$/;
5
+ // const humanDate = /^((Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec|January|February|March|April|May|June|July|August|September|October|November|December)[\d\040,\/(nd|rd|st|th)]{8,12}|[\d\040,\/(nd|rd|st|th)]{1,5}(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec|January|February|March|April|May|June|July|August|September|October|November|December),?\040\d{2,4})$/;
6
+
7
+ export default {
8
+ url,
9
+ img,
10
+ path,
11
+ pdf
12
+ };
@@ -0,0 +1,5 @@
1
+ import path from 'path';
2
+
3
+ export default function resolveHomeDirPath(filename, homeDir) {
4
+ return path.normalize(path.join(homeDir, filename));
5
+ }
@@ -0,0 +1,14 @@
1
+ import fs from 'fs/promises';
2
+ import { resolveHomeDirPath } from '.';
3
+
4
+ export default async function writeCache(data, homeDir) {
5
+ const cachePath = resolveHomeDirPath('.cache.json', homeDir);
6
+
7
+ try {
8
+ const jsonData = JSON.stringify(data, null, 2);
9
+ await fs.writeFile(cachePath, jsonData, 'utf8');
10
+ console.log('Cache updated');
11
+ } catch (error) {
12
+ console.error('Error updating cache data:', error);
13
+ }
14
+ }
@@ -0,0 +1,71 @@
1
+ import { loadCache, writeCache, processMarkdownFiles } from '$lib/utilities';
2
+ import { access } from 'fs/promises';
3
+ import { constants } from 'fs';
4
+ import { normalize, join, basename } from 'path';
5
+
6
+ export const prerender = true;
7
+ // export const csr = false;
8
+ // export const ssr = false;
9
+
10
+ let contentCache;
11
+ let contentCacheTime;
12
+
13
+ async function checkFileExists(filePath, homeDir) {
14
+ try {
15
+ await access(join(homeDir, filePath), constants.F_OK);
16
+ return true;
17
+ } catch {
18
+ return false;
19
+ }
20
+ }
21
+
22
+ /** @type {import('./$types').PageLoad} */
23
+ export async function load() {
24
+ const startLoad = performance.now();
25
+ const now = Date.now();
26
+ const contentCacheDuration = 60000; // Cache for 1 minute
27
+ const initialURLCache = await loadCache($home[0]);
28
+
29
+ const cssExists = await checkFileExists(normalize('/assets/styles.css'), $home[0]);
30
+
31
+ if (cssExists) console.log('Found CSS file at /assets/styles.css');
32
+ else console.log('No CSS file found as /assets/styles.css');
33
+
34
+ const faviconExists = await checkFileExists(normalize('/assets/favicon.png'), $home[0]);
35
+
36
+ const files = {
37
+ css: {
38
+ exists: cssExists,
39
+ path: join($home[0], 'assets', 'styles.css')
40
+ },
41
+ favicon: {
42
+ exists: faviconExists,
43
+ path: join($home[0], 'assets', 'favicon.png')
44
+ }
45
+ };
46
+
47
+ let data = {};
48
+
49
+ if (contentCache && now - contentCacheTime < contentCacheDuration) {
50
+ console.log('Using content cache');
51
+ data = contentCache;
52
+ } else {
53
+ console.log('Not using content cache');
54
+ data = await processMarkdownFiles(initialURLCache);
55
+ contentCache = data;
56
+ contentCacheTime = now;
57
+ }
58
+
59
+ // const { folder: website, finalCache } = await processMarkdownFiles(initialCache);
60
+ const { folder: website, finalCache } = data;
61
+
62
+ if (JSON.stringify(initialURLCache) !== JSON.stringify(finalCache)) {
63
+ writeCache(finalCache, $home[0]);
64
+ }
65
+
66
+ console.log(`Load time: ${(performance.now() - startLoad).toFixed(2)} ms`);
67
+
68
+ const folderName = basename($home[0]);
69
+
70
+ return { website, homeDir: $home[0], folderName, files };
71
+ }
@@ -0,0 +1,22 @@
1
+ import { getPagesByFolder, processMarkdownFiles, loadCache } from '../../lib/utilities';
2
+
3
+ /** @type {import('./$types').PageLoad} */
4
+ export async function load({ params }) {
5
+ const { path } = params;
6
+ const segments = path ? params.path.split('/') : [];
7
+ return { path, segments };
8
+ }
9
+
10
+ export const prerender = true;
11
+
12
+ export async function entries() {
13
+ const initialCache = await loadCache($home[0]);
14
+
15
+ const { folder: website } = await processMarkdownFiles(initialCache);
16
+
17
+ const pages = getPagesByFolder(website, '/', false).map((page) => {
18
+ return { path: page.url?.replace('/', '') };
19
+ });
20
+
21
+ return pages;
22
+ }