vowel 0.1.42 → 0.1.44

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
4
4
 
5
+ ## [0.1.44](https://github.com/samlfair/vowel/compare/v0.1.43...v0.1.44) (2024-10-21)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * metadata retrieval bug ([566f7cf](https://github.com/samlfair/vowel/commit/566f7cf64be2749e30cddcb474f6385638c408a9))
11
+ * webmentions bug ([2215ff5](https://github.com/samlfair/vowel/commit/2215ff5f5668d140535f6df3d30754fd685f22ae))
12
+
13
+ ## [0.1.43](https://github.com/samlfair/vowel/compare/v0.1.42...v0.1.43) (2024-10-18)
14
+
5
15
  ## [0.1.42](https://github.com/samlfair/vowel/compare/v0.1.41...v0.1.42) (2024-10-18)
6
16
 
7
17
 
package/bin.js CHANGED
@@ -28,7 +28,7 @@ if (args._.includes('publish')) {
28
28
  };
29
29
 
30
30
  if (!args.verbose) {
31
- filterConsole(['jsconfig', 'vite', 'Vite', 'tsconfig', `--host`]);
31
+ // filterConsole(['jsconfig', 'vite', 'Vite', 'tsconfig', `--host`]);
32
32
  console.log(`\n\n`);
33
33
  }
34
34
 
@@ -54,7 +54,7 @@ child.stdout.on('data', (data) => {
54
54
  console.log(`\n\n`);
55
55
  processedFiles = 0;
56
56
  foundFiles = 0;
57
- } else if(false) { // Toggle to true to reveal all console output
57
+ } else if(true) { // Toggle to true to reveal all console output
58
58
  console.log(message)
59
59
  } else if (message.match('http://localhost:')) {
60
60
  const url = message.match(/http:\/\/localhost:\S+/);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vowel",
3
- "version": "0.1.42",
3
+ "version": "0.1.44",
4
4
  "homepage": "https://vowel.cc",
5
5
  "author": "Sam Littlefair (https://littlefair.ca)",
6
6
  "repository": {
@@ -13,8 +13,6 @@
13
13
  // TODO: Add conditional logic for format = 'xml'
14
14
 
15
15
  const headings = ast.filter((child) => child.type === 'heading');
16
-
17
- console.log(webmentions)
18
16
  </script>
19
17
 
20
18
  <!-- Image -->
@@ -0,0 +1,46 @@
1
+ import { XMLParser } from "fast-xml-parser"
2
+ import urlMetadata from 'url-metadata';
3
+
4
+ export default async function getMetadata({ cache, url }) {
5
+ if (!cache[url]) {
6
+ try {
7
+ const urlObject = new URL(url);
8
+ const allMetadata = await urlMetadata(urlObject.href, {
9
+ includeResponseBody: true,
10
+ ensureSecureImageRequest: true
11
+ });
12
+
13
+ const parser = new XMLParser({
14
+ unpairedTags: ["!doctype", "meta", "link", "hr", "br", "img"],
15
+ ignoreAttributes: false,
16
+ stopNodes: ["*.pre", "*.script"],
17
+ processEntities: true,
18
+ htmlEntities: true
19
+ })
20
+
21
+ let parsedData = parser.parse(allMetadata.responseBody)
22
+ const webmentionEndpoint = parsedData?.html?.head?.link?.find(link => {
23
+ return link["@_rel"] === "webmention"
24
+ })?.["@_href"]
25
+
26
+ const metadata = {
27
+ image: allMetadata['og:image'],
28
+ ogURL: allMetadata['og:url'],
29
+ canonicalURL: allMetadata.canonical,
30
+ title: allMetadata.title,
31
+ ogTitle: allMetadata['og:title'],
32
+ author: allMetadata.author,
33
+ description: allMetadata.description,
34
+ webmentionEndpoint
35
+ };
36
+
37
+ cache[url] = metadata;
38
+
39
+ return { metadata }
40
+ } catch (error) {
41
+ console.log({ fetchingMetadataError: error })
42
+ return { metadata: undefined }
43
+ }
44
+ }
45
+ return { metadata: cache[url] }
46
+ }
@@ -18,4 +18,5 @@ export { default as resolveHomeDirPath } from './resolveHomeDirPath';
18
18
  export { default as createPageClass } from './createPageClass';
19
19
  export { default as createFolderClass } from './createFolderClass';
20
20
  export { default as isActiveLink } from './isActiveLink';
21
- export { default as sendWebmention } from "./sendWebmention"
21
+ export { default as sendWebmention } from "./sendWebmention"
22
+ export { default as getMetadata } from "./getMetadata"
@@ -1,5 +1,4 @@
1
- import urlMetadata from 'url-metadata';
2
- import { sendWebmention } from './';
1
+ import { sendWebmention, getMetadata } from './';
3
2
 
4
3
  function isURL(string) {
5
4
  try {
@@ -10,8 +9,9 @@ function isURL(string) {
10
9
  }
11
10
  }
12
11
 
13
- export default async function mutateMarkdownAST(ast, cache, webmentions, pageURL) {
14
12
 
13
+
14
+ export default async function mutateMarkdownAST(ast, cache, webmentions, pageURL) {
15
15
  const promises = ast.map(async (node) => {
16
16
  // TODO: Improve this URL regex
17
17
  if (node.type === 'paragraph') {
@@ -25,55 +25,32 @@ export default async function mutateMarkdownAST(ast, cache, webmentions, pageURL
25
25
  node.type = 'url';
26
26
  const { url } = node.children[0];
27
27
  node.url = url
28
- if (!cache[node.url]) {
29
- try {
30
- const urlObject = new URL(url);
31
- const response = await urlMetadata(urlObject.href, {
32
- includeResponseBody: true,
33
- ensureSecureImageRequest: true
34
- });
35
28
 
36
- console.log(!!webmentions)
37
- console.log({ webmentions })
38
- console.log(webmentions.find(webmention => webmention.target === url))
29
+ const { metadata } = await getMetadata({ cache, url })
39
30
 
31
+ node.metadata = metadata;
32
+ node.value = url;
33
+ delete node.children;
40
34
 
41
- if (webmentions && !webmentions.find(webmention => webmention.target === url)) {
42
- webmentions.push({
43
- target: url,
44
- status: "new"
45
- })
46
- } else if (webmentions) {
47
- const webmention = webmentions.find(webmention => webmention.target === url)
48
- if (webmention.status === "new") {
49
- webmention.status = await sendWebmention({ body: response.responseBody, target: webmention.target, source: pageURL })
50
- }
35
+ try {
36
+ console.log("Looking for webmentions")
37
+ if (webmentions && !webmentions.find(webmention => webmention.target === url)) {
38
+ console.log("Found new page")
39
+ webmentions.push({
40
+ target: url,
41
+ status: "new"
42
+ })
43
+ } else if (webmentions) {
44
+ console.log("Found new webmention")
45
+ const webmention = webmentions.find(webmention => webmention.target === url)
46
+ if (webmention.status === "new" || webmention.status === "failure" || webmention.status === "429") {
47
+ console.log("Sending webmention")
48
+ webmention.status = await sendWebmention({ endpoint: metadata.webmentionEndpoint, target: webmention.target, source: pageURL })
51
49
  }
52
-
53
- const metadata = {
54
- image: response['og:image'],
55
- ogURL: response['og:url'],
56
- canonicalURL: response.canonical,
57
- title: response.title,
58
- ogTitle: response['og:title'],
59
- author: response.author,
60
- description: response.description
61
- };
62
-
63
-
64
-
65
- cache[url] = metadata;
66
- node.metadata = metadata;
67
- node.value = url;
68
-
69
- delete node.children;
70
- } catch (error) {
71
- node.value = url;
72
50
  }
73
- } else {
74
- node.metadata = cache[url];
51
+ } catch (error) {
52
+ console.log({ webmentionError: error })
75
53
  }
76
-
77
54
  } else if (node.children[0].type === 'image') {
78
55
  node.type = 'figure';
79
56
  if (node?.children[1]?.type === 'text') {
@@ -1,5 +1,5 @@
1
1
  import urlMetadata from 'url-metadata';
2
- import { regexPatterns, isObject, parseDate, sendWebmention } from '.';
2
+ import { regexPatterns, isObject, parseDate, sendWebmention, getMetadata } from '.';
3
3
 
4
4
  /**
5
5
  * Description
@@ -74,62 +74,32 @@ export default async function mutateMarkdownFrontmatter(frontmatter, cache, webm
74
74
  break;
75
75
  }
76
76
  case 'url': {
77
+ const { metadata } = await getMetadata({
78
+ cache,
79
+ url: input
80
+ })
77
81
 
78
- if (!cache[input]) {
79
- try {
80
- const url = new URL(input);
81
- const metadata = await urlMetadata(url.href, {
82
- includeResponseBody: true,
83
- ensureSecureImageRequest: true
84
- });
85
-
86
- const selectedMetadata = {
87
- url: metadata.url,
88
- canonical: metadata.canonical,
89
- title: metadata.title,
90
- image: metadata.image,
91
- favicons: url.favicons,
92
- og_url: url['og:url'],
93
- og_title: url['og:title'],
94
- og_description: url['og:description'],
95
- og_site_name: url['og:side_name'],
96
- og_image: url['og:image']
97
- };
82
+ frontmatter[key] = {
83
+ type: 'url',
84
+ output: metadata || input,
85
+ input
86
+ };
98
87
 
99
- if (webmentions && !webmentions.find(webmention => webmention.target === input)) {
100
- webmentions.push({
101
- target: input,
102
- status: "new"
103
- })
104
- } else if (webmentions) {
105
- const webmention = webmentions.find(webmention => webmention.target === input)
106
- if (webmention.status === "new") {
107
- webmention.status = await sendWebmention({ body: metadata.responseBody, target: webmention.target, source: pageURL })
108
- }
88
+ try {
89
+ if (webmentions && !webmentions.find(webmention => webmention.target === input)) {
90
+ webmentions.push({
91
+ target: input,
92
+ status: "new"
93
+ })
94
+ } else if (webmentions) {
95
+ const webmention = webmentions.find(webmention => webmention.target === input)
96
+ if (webmention.status === "new") {
97
+ webmention.status = await sendWebmention({ endpoint: metadata.webmentionEndpoint, target: webmention.target, source: pageURL })
109
98
  }
110
-
111
-
112
- cache[input] = selectedMetadata;
113
-
114
- frontmatter[key] = {
115
- type: 'url',
116
- output: selectedMetadata,
117
- input
118
- };
119
- } catch (e) {
120
- console.error(`Error on URL in frontmatter: ${frontmatter[key]}`);
121
- frontmatter[key] = {
122
- type: 'string',
123
- output: input,
124
- input
125
- };
126
99
  }
127
- } else {
128
- frontmatter[key] = {
129
- type: 'url',
130
- output: cache[input],
131
- input
132
- };
100
+
101
+ } catch (error) {
102
+ console.log({ webmentionsError: error })
133
103
  }
134
104
  break;
135
105
  }
@@ -90,7 +90,7 @@ function DefaultDirectory(path) {
90
90
  * @param {import('./loadCache').Cache} cache
91
91
  * @returns {Promise<Directory>}
92
92
  */
93
- async function readFolder(folderPath, parents, cache, hidden, publishedData) {
93
+ async function readFolder(folderPath, parents, cache, hidden, publishedData, domain) {
94
94
  function filterFiles(file) {
95
95
  if (file.name.startsWith('.')) return false;
96
96
  if (file.name.startsWith('README')) return false;
@@ -106,7 +106,7 @@ async function readFolder(folderPath, parents, cache, hidden, publishedData) {
106
106
  console.log(`filesfound:${files.length}`);
107
107
 
108
108
  const promises = files.map(
109
- async (file) => await readFile(file, parents, cache, folderPath, hidden, publishedData)
109
+ async (file) => await readFile(file, parents, cache, folderPath, hidden, publishedData, domain)
110
110
  );
111
111
 
112
112
  const folder = (await Promise.all(promises)).reduce((acc, obj) => ({ ...acc, ...obj }), {});
@@ -125,16 +125,28 @@ async function readFolder(folderPath, parents, cache, hidden, publishedData) {
125
125
  return folder;
126
126
  }
127
127
 
128
- async function readFile(file, parents, cache, folderPath, hidden, publishedData) {
128
+ function buildHREF(url, domain) {
129
+ try {
130
+ const urlObj = new URL(domain)
131
+ urlObj.pathname = url
132
+ return urlObj.href
133
+ } catch (e) {
134
+ return undefined
135
+
136
+ }
137
+ }
138
+
139
+ async function readFile(file, parents, cache, folderPath, hidden, publishedData, domain) {
129
140
  const route = path.parse(file.name).name;
130
141
  const url = buildURL(parents, route);
131
142
  const filePath = path.join(folderPath, file.name);
132
-
143
+
133
144
  const extension = path.extname(file.name);
134
-
145
+
135
146
  const hide = (file.name.startsWith('$') && file.name.length > 1) || hidden;
136
-
147
+
137
148
  if (file.isFile() && extension === '.md') {
149
+ const href = buildHREF(url, domain)
138
150
  const startLoad = performance.now();
139
151
  const shortPath = parents + '/' + file.name;
140
152
 
@@ -147,7 +159,7 @@ async function readFile(file, parents, cache, folderPath, hidden, publishedData)
147
159
  filePublishedData = publishedData.at(-1)
148
160
  }
149
161
 
150
- const { ast, frontmatter, imputedProperties } = await readMarkdownFile(filePath, cache, filePublishedData, url);
162
+ const { ast, frontmatter, imputedProperties } = await readMarkdownFile(filePath, cache, filePublishedData, href);
151
163
 
152
164
  const loadTime = (performance.now() - startLoad).toFixed(2);
153
165
  // console.log(`📄 ${shortPath} (${loadTime}ms)`);
@@ -189,7 +201,7 @@ async function readFile(file, parents, cache, folderPath, hidden, publishedData)
189
201
  const shortPath = parents + '/' + file.name;
190
202
 
191
203
  const startLoad = performance.now();
192
- const folder = await readFolder(path.join(folderPath, file.name), url, cache, hide, publishedData);
204
+ const folder = await readFolder(path.join(folderPath, file.name), url, cache, hide, publishedData, domain);
193
205
 
194
206
  const loadTime = (performance.now() - startLoad).toFixed(2);
195
207
  // console.log(`📁 ${shortPath} (${loadTime}ms)`);
@@ -221,11 +233,11 @@ async function readFile(file, parents, cache, folderPath, hidden, publishedData)
221
233
  * @param {import('./loadCache').Cache} cache
222
234
  * @returns {Promise<ProcessedFiles>}
223
235
  */
224
- export default async function processMarkdownFiles(cache, publishedData) {
236
+ export default async function processMarkdownFiles(cache, publishedData, domain) {
225
237
  /** @type {Array<string>} */
226
238
  // @ts-ignore
227
239
  const [homeDir] = $home;
228
- const folder = await readFolder(homeDir, '', cache, null, publishedData);
240
+ const folder = await readFolder(homeDir, '', cache, null, publishedData, domain);
229
241
 
230
242
  return { folder, finalCache: cache };
231
243
  }
@@ -1,28 +1,11 @@
1
- import { XMLParser } from "fast-xml-parser"
2
-
3
-
4
- export default async function sendWebmention({ body, source, target }) {
1
+ export default async function sendWebmention({ endpoint, source, target }) {
5
2
  try {
6
- const parser = new XMLParser({
7
- unpairedTags: ["!doctype", "meta", "link", "hr", "br", "img"],
8
- ignoreAttributes: false,
9
- stopNodes: ["*.pre", "*.script"],
10
- processEntities: true,
11
- htmlEntities: true
12
- })
13
-
14
- let parsedData = parser.parse(body)
15
- const webmentionEndpoint = parsedData?.html?.head?.link?.find(link => {
16
- return link["@_rel"] === "webmention"
17
- })?.["@_href"]
18
-
19
- if (webmentionEndpoint) {
3
+ if (endpoint) {
20
4
  const params = new URLSearchParams()
21
5
  params.append('source', source)
22
6
  params.append('target', target)
23
7
 
24
-
25
- const response = await fetch(webmentionEndpoint, {
8
+ const response = await fetch(endpoint, {
26
9
  method: "POST",
27
10
  body: params,
28
11
  headers: {
@@ -30,16 +13,22 @@ export default async function sendWebmention({ body, source, target }) {
30
13
  }
31
14
  })
32
15
 
16
+ console.log({
17
+ source, target,
18
+ webmentionResponse: await response.json()
19
+ })
20
+
33
21
  if(response.status === 429) {
34
22
  return "429"
35
23
  } else if (response.status >= 200 && response.status < 300) {
36
24
  return "success"
37
25
  } else {
26
+ console.log({ failure: await response.json() })
38
27
  return "failure"
39
28
  }
40
29
  }
41
- } catch (e) {
42
- console.log(e)
30
+ } catch (error) {
31
+ console.log({ error })
43
32
  return "error"
44
33
  }
45
34
  }
@@ -25,22 +25,22 @@ export async function GET() {
25
25
  // everything.request.url
26
26
 
27
27
  let settings
28
-
28
+
29
29
  const settingsPath = join($home[0], "/settings.md")
30
30
  const settingsExists = await checkFileExists(settingsPath);
31
- if(settingsExists) {
31
+ if (settingsExists) {
32
32
  settings = await readMarkdownFile(settingsPath)
33
33
  }
34
34
 
35
35
  // Get published articles from live site
36
36
  const publishedData = await getPublishedData(settings.frontmatter.domain)
37
37
 
38
- console.log({publish: $publish[0]})
39
-
40
- if($publish[0]) {
38
+ // if($publish[0]) {
39
+ if (true) {
41
40
  const initialCache = await loadCache($home[0]);
42
-
43
- const { folder: website } = await processMarkdownFiles(initialCache, publishedData);
41
+
42
+
43
+ const { folder: website } = await processMarkdownFiles(initialCache, publishedData, settings.frontmatter.domain);
44
44
  }
45
45
 
46
46
  const headers = {
@@ -48,8 +48,6 @@ export async function GET() {
48
48
  'Content-Type': 'text/plain'
49
49
  };
50
50
 
51
- console.log({ publishedData })
52
-
53
51
  const response = new Response(JSON.stringify(publishedData), {
54
52
  headers
55
53
  });