vowel 0.3.4 → 0.4.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "vowel",
3
3
  "type": "module",
4
- "version": "0.3.4",
4
+ "version": "0.4.0",
5
5
  "bin": "bin.js",
6
6
  "main": "index.js",
7
7
  "scripts": {
@@ -19,7 +19,7 @@ import { h } from 'hastscript'
19
19
  import { normalizeHeadings } from 'mdast-normalize-headings'
20
20
  import { readFileSync } from "fs"
21
21
  import { remove } from "unist-util-remove"
22
- import { testURL } from "./../../utils.js"
22
+ import { testURL, testHashtags, createHashtagPage, toTitleCase, hashtagRegexSingle } from "./../../utils.js"
23
23
  import { toHast } from 'mdast-util-to-hast'
24
24
  import { toString as hastToString } from 'hast-util-to-string'
25
25
  import { toString as mdastToString } from 'mdast-util-to-string'
@@ -103,14 +103,6 @@ function makeHead(metadata, url) {
103
103
  return treeMainHead
104
104
  }
105
105
 
106
- function toTitleCase(string) {
107
- if (!string) return
108
- return string.split(" ").map(word => {
109
- const letters = word.split("")
110
- letters[0] = letters[0].toUpperCase()
111
- return letters.join("")
112
- }).join(" ")
113
- }
114
106
 
115
107
  function readURL(data) {
116
108
  const hast = fromHtml(data)
@@ -140,7 +132,7 @@ function readURL(data) {
140
132
 
141
133
 
142
134
  /** @type {Votive.ReadText} */
143
- function readMarkdown(string, filePath, destinationPath, database, config) {
135
+ function readFile(string, filePath, destinationPath, database, config) {
144
136
  const mdast = fromMarkdown(string, {
145
137
  // Micromark extensions
146
138
  extensions: [
@@ -198,6 +190,7 @@ function readMarkdown(string, filePath, destinationPath, database, config) {
198
190
  visit(mdast, (node, index, parent) => {
199
191
  if (node.type === "text" && parent.children.length === 1 && parent.type === "paragraph") {
200
192
  const validURL = testURL(node.value)
193
+
201
194
  if (validURL) {
202
195
 
203
196
  jobs.push({
@@ -205,6 +198,107 @@ function readMarkdown(string, filePath, destinationPath, database, config) {
205
198
  runner: "text",
206
199
  destination: destinationPath
207
200
  })
201
+
202
+ return
203
+ }
204
+
205
+ const hashtags = testHashtags(node.value)
206
+
207
+ // TODO: Tags should not appear in menus
208
+ // TODO: Make this work when tags are embedded in text
209
+
210
+ if (hashtags) {
211
+ function convertTagsToLinks(value) {
212
+ const match = value.match(hashtagRegexSingle)
213
+ if (match) {
214
+ const remainder = value.slice(match[0].length)
215
+ const child = {
216
+ type: "link",
217
+ url: `/tags/${match[3]}`,
218
+ children: [
219
+ {
220
+ type: "text",
221
+ value: match[0]
222
+ }
223
+ ]
224
+ }
225
+
226
+ return [
227
+ {
228
+ type: "text",
229
+ value: match[1]
230
+ },
231
+ child,
232
+ {
233
+ type: "text",
234
+ value: match[4]
235
+ },
236
+
237
+ ...convertTagsToLinks(remainder)
238
+ ]
239
+ }
240
+ return [
241
+ {
242
+ type: "text",
243
+ value: value
244
+ }
245
+ ]
246
+ }
247
+
248
+ parent.children = convertTagsToLinks(node.value)
249
+
250
+ const markdown = `/tags/**`
251
+ const mdast = fromMarkdown(markdown)
252
+
253
+ const extant = database.target.get("tags.html")
254
+
255
+ // FIXME: Update abstract format
256
+ if (!extant) {
257
+ // Delete if unnecessary
258
+ database.target.create({
259
+ path: `tags.html`,
260
+ abstract: mdast,
261
+ metadata: {
262
+ breadcrumb: "Tags",
263
+ title: "Tags",
264
+ prettyURL: "/tags",
265
+ }
266
+ })
267
+ }
268
+
269
+ database.dependency.track({}, "tags", null, destinationPath, "tags.html")
270
+ database.target.markStale("tags.html")
271
+
272
+ if (hashtags) {
273
+ if (!metadata.tags) {
274
+ metadata.tags = []
275
+ } else if (!Array.isArray(metadata.tags)) {
276
+ metadata.tags = []
277
+ }
278
+
279
+ metadata.tags = []
280
+
281
+ hashtags.forEach(hashtag => {
282
+ const title = toTitleCase(hashtag)
283
+ metadata.tags.push(hashtag)
284
+
285
+ const abstract = createHashtagPage(hashtag)
286
+
287
+ const tagMetadata = {
288
+ breadcrumb: title,
289
+ title: title,
290
+ prettyURL: `/tags/${hashtag}`,
291
+ type: "tag",
292
+ tag: hashtag
293
+ }
294
+
295
+ const created = database.target.create({
296
+ path: `tags/${hashtag}.html`,
297
+ abstract,
298
+ metadata: tagMetadata
299
+ })
300
+ })
301
+ }
208
302
  }
209
303
  }
210
304
  })
@@ -364,7 +458,7 @@ function getMetadata(tree, filePath) {
364
458
  }
365
459
 
366
460
  /** @type {Votive.ReadAbstract} */
367
- function readAbstract(abstract, database, config) {
461
+ function transformFile(abstract, database, config) {
368
462
  const jobs = []
369
463
  return { abstract, jobs }
370
464
  }
@@ -580,13 +674,31 @@ function readFolder(folder, database, config, isRoot) {
580
674
  }
581
675
 
582
676
  /** @type {Votive.ProcessorWrite} */
583
- function writeHTML(destination, database, config) {
677
+ function writeFile(destination, database, config) {
584
678
  const isRoot = destination.path === "index.html"
585
679
 
680
+ // console.log(destination.metadata)
681
+
682
+ if (destination.metadata.type === "tag") {
683
+ if (!destination.metadata.tag) return false
684
+
685
+ const pages = database.target.getManyWithTrackers({
686
+ recursive: true,
687
+ dependent: destination,
688
+ query: {
689
+ tags: destination.metadata.tag
690
+ }
691
+ })
692
+
693
+ if (!pages.length) return false
694
+ }
695
+
586
696
  const settings = database.setting.getByFolder(destination.dir + path.sep)
587
697
 
588
698
  const { abstract, metadata, ...rest } = destination
589
699
 
700
+ if (metadata.tags) metadata.tags = JSON.parse(metadata.tags)
701
+
590
702
  /** @param {string} filePath */
591
703
  function listFolders(filePath) {
592
704
  if (!filePath) return []
@@ -907,10 +1019,15 @@ function writeHTML(destination, database, config) {
907
1019
  const folder = path.relative("/", dir)
908
1020
  // const url = new URL(child.value, "thismessage://")
909
1021
  const count = url.searchParams.get("count")
1022
+ const tag = url.searchParams.get("tag")
1023
+ const query = tag
1024
+ ? { tags: tag }
1025
+ : {}
1026
+
910
1027
  const targets = database.target.getManyWithTrackers({
911
1028
  folder,
912
1029
  recursive,
913
- query: {},
1030
+ query,
914
1031
  dependent: destination.path
915
1032
  })
916
1033
 
@@ -1152,26 +1269,26 @@ function router(args) {
1152
1269
 
1153
1270
 
1154
1271
  /** @type {Votive.VotiveProcessor} */
1155
- const markdownReader = {
1272
+ const readMarkdown = {
1156
1273
  extensions: [".md"],
1157
1274
  format: "text",
1158
- readFile: readMarkdown,
1275
+ readFile,
1159
1276
  readResource: readURL,
1160
- transformFile: readAbstract,
1161
- readFolder: readFolder,
1277
+ transformFile,
1278
+ readFolder,
1162
1279
  }
1163
1280
 
1164
- const htmlWriter = {
1281
+ const writeHTML = {
1165
1282
  extensions: [".html"],
1166
1283
  format: "text",
1167
- writeFile: writeHTML
1284
+ writeFile
1168
1285
  }
1169
1286
 
1170
1287
 
1171
1288
  /** @type {Votive.VotivePlugin} */
1172
1289
  const vowelMarkdownPlugin = {
1173
1290
  name: "vowel",
1174
- processors: [markdownReader, htmlWriter],
1291
+ processors: [readMarkdown, writeHTML],
1175
1292
  router
1176
1293
  }
1177
1294
 
package/utils.js CHANGED
@@ -1,3 +1,8 @@
1
+ import {fromMarkdown} from 'mdast-util-from-markdown'
2
+ import {toHast} from 'mdast-util-to-hast'
3
+ import {toHtml} from 'hast-util-to-html'
4
+
5
+
1
6
  /** @param {string} text */
2
7
  export function testURL(text) {
3
8
  if (text.match(/^https?:\/\/[\S]+$/)) {
@@ -8,3 +13,28 @@ export function testURL(text) {
8
13
  }
9
14
  }
10
15
  }
16
+
17
+ export const hashtagRegexSingle = /(^|\s)(#([\w\-\/]+))($|\b)/
18
+ export const hashtagRegexGlobal = /(?:^|\s)(#[\w\-\/]+)(?:$|\b)/g
19
+
20
+ export function testHashtags(text) {
21
+ const match = text.match(hashtagRegexGlobal)
22
+ if(match) return match.map(a => a.trim().slice(1))
23
+ }
24
+
25
+
26
+ export function toTitleCase(string) {
27
+ if (!string) return
28
+ return string.split(" ").map(word => {
29
+ const letters = word.split("")
30
+ letters[0] = letters[0].toUpperCase()
31
+ return letters.join("")
32
+ }).join(" ")
33
+ }
34
+
35
+ export function createHashtagPage(tag) {
36
+ const markdown = `# ${toTitleCase(tag)}\n\n/**?tag=${tag}`
37
+ const mdast = fromMarkdown(markdown)
38
+
39
+ return mdast
40
+ }