votive 0.0.7 → 0.0.9

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,6 +1,6 @@
1
1
  {
2
2
  "name": "votive",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "A file processor.",
5
5
  "homepage": "https://github.com/samlfair/votive#readme",
6
6
  "bugs": {
@@ -24,6 +24,7 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "encoding-sniffer": "^0.2.1",
27
+ "p-limit": "^7.3.0",
27
28
  "unified": "^11.0.5",
28
29
  "unist-util-visit": "^5.0.0",
29
30
  "workerpool": "^10.0.1"
package/tests/index.js CHANGED
@@ -13,325 +13,365 @@ import { checkFile } from "./../lib/utils/index.js"
13
13
 
14
14
  process.chdir("./tests")
15
15
 
16
- test("empty directory", async () => {
17
- const tempDest = fs.mkdtempDisposableSync("destination-")
18
- const tempSource = fs.mkdtempDisposableSync("source-")
19
-
20
- try {
21
- const stop = stopwatch("Bundle empty folder")
22
- await votive.bundle({
23
- sourceFolder: tempSource.path,
24
- destinationFolder: tempDest.path,
25
- plugins: []
16
+ test("database creation in memory", (t) => {
17
+ const database = votive.createDatabase(":memory:")
18
+
19
+ t.test("database functions", () => {
20
+ const databaseKeys = new Set(Object.keys(database))
21
+ const expectedKeys = new Set(["saveDB", "source", "setting", "dependency", "target", "url"])
22
+ const difference = databaseKeys.difference(expectedKeys)
23
+ assert(difference.size === 0)
24
+ })
25
+
26
+ t.test("create sources", () => {
27
+ database.source.create("abc.in", "abc.out", 1)
28
+ database.source.create("def.in", "def.out", 2)
29
+ const sources = database.source.getAll()
30
+ assert(sources.length === 2)
31
+ })
32
+
33
+ t.test("create targets", () => {
34
+ database.target.create({
35
+ path: "abc.out",
36
+ abstract: ["abc"],
37
+ metadata: {
38
+ color: "blue",
39
+ }
26
40
  })
27
41
 
28
- stop()
29
-
30
- const dir = fs.readdirSync(tempDest.path)
31
- assert(dir.length === 0, "Destination directory is not empty.")
32
- } catch (error) {
33
- tempDest.remove()
34
- tempSource.remove()
35
- console.error(error)
36
-
37
- }
38
-
39
- tempDest.remove()
40
- tempSource.remove()
41
- })
42
-
43
- test("internals", async (t) => {
44
- const dbExists = checkFile(".votive.db")
45
- if (dbExists) await rm(".votive.db")
46
-
47
- const temp = fs.mkdtempDisposableSync("destination-")
48
-
49
- /** @returns {Job} */
50
- function createExampleJob() {
51
- return {
52
- data: Math.floor(Math.random() * 1000),
53
- runner: "exampleRunner"
54
- }
55
- }
56
-
57
-
58
- /** @type {ReadText} */
59
- function exampleTextReader(text, filePath, destinationPath, database, config) {
60
- const matches = text.match(/\b\w+\b/)
61
- const title = matches ? matches[0] : "Untitled"
62
-
63
- /** @type {ReadTextResult} */
64
- return {
65
- abstract: {
66
- content: text,
67
- },
42
+ database.target.create({
43
+ path: "def.out",
44
+ abstract: ["def"],
68
45
  metadata: {
69
- title
70
- },
71
- jobs: [
72
- createExampleJob()
73
- ]
74
- }
75
- }
76
-
77
- /** @type {ReadAbstract} */
78
- function exampleAbstractReader(abstract, database, config) {
79
- abstract.exampleAppend = true
80
- return { abstract, jobs: [createExampleJob()] }
81
- }
82
-
83
- /** @type {ReadFolder} */
84
- function exampleFolderReader(folder, database, config) {
85
- return {
86
- jobs: [createExampleJob()],
87
- destinations: [
88
- {
89
- path: "index.html",
90
- abstract: { content: "" },
91
- metadata: {
92
- title: "home"
93
- },
94
- syntax: "md"
95
- }
96
- ]
97
- }
98
- }
99
-
100
- /** @type {VotiveProcessor} */
101
- const exampleProcessor = {
102
- syntax: "md",
103
- filter: {
104
- extensions: [".md"]
105
- },
106
- read: {
107
- text: exampleTextReader,
108
- abstract: exampleAbstractReader,
109
- folder: exampleFolderReader
110
- },
111
- write: (destination, database, config) => {
112
- return {
113
- data: "lorem ipsum",
46
+ color: "red",
114
47
  }
115
- }
116
- }
117
-
118
- /** @param {string} sourcePath */
119
- function router({ base, ...parsed }) {
120
- return { ...parsed, ext: ".html" }
121
- }
48
+ })
122
49
 
123
- /** @type {VotivePlugin} */
124
- const examplePlugin = {
125
- name: "example plugin",
126
- runners: {
127
- exampleRunner: exampleRunner
128
- },
129
- router,
130
- processors: [exampleProcessor]
131
- }
50
+ const targets = database.target.getAll()
132
51
 
133
- /** @type {Runner} */
134
- async function exampleRunner(data, database) {
135
- return await new Promise((resolve) => setTimeout(() => resolve(data), 1))
136
- }
52
+ assert(targets.length === 2)
53
+ })
54
+ })
137
55
 
138
- /** @type {VotiveConfig} */
139
- const config = {
140
- sourceFolder: "./markdown",
141
- destinationFolder: temp.path,
142
- plugins: [examplePlugin]
143
- }
56
+ test("bundle", async () => {
57
+ const tempDest = fs.mkdtempDisposableSync("target-")
58
+ const tempSource = fs.mkdtempDisposableSync("source-")
144
59
 
145
- /** @type {FlatProcessors} */
146
- const processors = [
147
- {
148
- plugin: examplePlugin,
149
- processor: exampleProcessor
150
- }
151
- ]
60
+ fs.writeFileSync(
61
+ path.join(tempSource.path, "abc.in"),
62
+ JSON.stringify({ abstract: ["abc"], metadata: { color: "blue" } }),
63
+ "utf-8"
64
+ )
152
65
 
153
66
  try {
154
- fs.writeFileSync("markdown/prunee.md", "A little content")
155
-
156
- const database = votive.createDatabase()
157
- const sourcesOne = database.getAllSources()
67
+ const stop = stopwatch("Bundle folder")
68
+ const database = await votive.bundle({
69
+ sourceFolder: tempSource.path,
70
+ destinationFolder: tempDest.path,
71
+ plugins: [{
72
+ name: "test-plugin",
73
+ processors: [{
74
+ format: "text",
75
+ extensions: [".in"],
76
+ // readResource: null,
77
+ writeFile: (destination) => ({ data: destination.abstract[0] }),
78
+ readFile: (data) => ({ abstract: [data], metadata: { color: "green" } }),
79
+ // readFolder: null,
80
+ // transformFile: null
81
+ }],
82
+ router: ({ base, dir, root }) => ({ base, root, ext: ".out" }),
83
+ }]
158
84
 
159
- t.test("no sources on startup", () => {
160
- const isArray = Array.isArray(sourcesOne)
161
- const isEmpty = !sourcesOne.length
162
- assert(isArray && isEmpty)
163
85
  })
164
86
 
87
+ const sources = database.source.getAll()
165
88
 
166
-
167
- const stop = stopwatch("Read sources")
168
-
169
- const { folders, sources } = await votive.readSources(config, database, processors)
170
-
171
- t.test('one folder exists', () => {
172
- assert(folders.length === 1)
173
- })
174
-
175
- t.test('three sources exist', () => {
176
- assert(sources.length === 4)
177
- })
89
+ const targets = database.target.getAll()
178
90
 
179
91
  stop()
180
92
 
181
- const sourcesJobs = sources.flatMap(source => source.jobs)
182
-
183
- const { processedAbstracts, abstractsJobs } = votive.readAbstracts(sources, config, database, processors)
184
-
185
- t.test('transformation succeeded', () => {
186
- assert(processedAbstracts.find(abstract => abstract.exampleAppend))
187
- })
188
-
189
- t.test('abstract jobs exist', () => {
190
- assert(abstractsJobs.length > 0)
191
- assert(abstractsJobs.every(job => job.data && job.runner))
192
- })
193
-
194
- const foldersJobs = readFolders(folders, config, database, processors)
93
+ const dir = fs.readdirSync(tempDest.path, { recursive: true })
94
+ assert(dir.length === 0, "Destination directory is not empty.")
95
+ } catch (error) {
96
+ console.error(error)
97
+ tempDest.remove()
98
+ tempSource.remove()
195
99
 
196
- t.test("folders jobs exist", () => {
197
- assert(foldersJobs.length > 0)
198
- assert(foldersJobs.every(job => job.data && job.runner))
199
- })
100
+ }
200
101
 
201
- database.createOrUpdateDestination({ metadata: { a: 1, b: 2 }, path: "abc.html", abstract: { c: 3 }, syntax: "md" })
202
- const firstDestination = database.getDestinationIndependently("abc.html", [])
102
+ tempDest.remove()
103
+ tempSource.remove()
104
+ })
203
105
 
204
- t.test('first destination created', () => {
205
- assert(firstDestination.path === 'abc.html')
206
- })
106
+ // test("internals", async (t) => {
107
+ // const dbExists = checkFile(".votive.db")
108
+ // if (dbExists) await rm(".votive.db")
109
+
110
+ // const temp = fs.mkdtempDisposableSync("destination-")
111
+
112
+ // /** @returns {Job} */
113
+ // function createExampleJob() {
114
+ // return {
115
+ // data: Math.floor(Math.random() * 1000),
116
+ // runner: "exampleRunner"
117
+ // }
118
+ // }
119
+
120
+
121
+ // /** @type {ReadText} */
122
+ // function exampleTextReader(text, filePath, destinationPath, database, config) {
123
+ // const matches = text.match(/\b\w+\b/)
124
+ // const title = matches ? matches[0] : "Untitled"
125
+
126
+ // /** @type {ReadTextResult} */
127
+ // return {
128
+ // abstract: {
129
+ // content: text,
130
+ // },
131
+ // metadata: {
132
+ // title
133
+ // },
134
+ // jobs: [
135
+ // createExampleJob()
136
+ // ]
137
+ // }
138
+ // }
139
+
140
+ // /** @type {ReadAbstract} */
141
+ // function exampleAbstractReader(abstract, database, config) {
142
+ // abstract.exampleAppend = true
143
+ // return { abstract, jobs: [createExampleJob()] }
144
+ // }
145
+
146
+ // /** @type {ReadFolder} */
147
+ // function exampleFolderReader(folder, database, config) {
148
+ // return {
149
+ // jobs: [createExampleJob()],
150
+ // destinations: [
151
+ // {
152
+ // path: "index.html",
153
+ // abstract: { content: "" },
154
+ // metadata: {
155
+ // title: "home"
156
+ // },
157
+ // syntax: "md"
158
+ // }
159
+ // ]
160
+ // }
161
+ // }
162
+
163
+ // /** @type {VotiveProcessor} */
164
+ // const exampleProcessor = {
165
+ // syntax: "md",
166
+ // filter: {
167
+ // extensions: [".md"]
168
+ // },
169
+ // read: {
170
+ // text: exampleTextReader,
171
+ // abstract: exampleAbstractReader,
172
+ // folder: exampleFolderReader
173
+ // },
174
+ // write: (destination, database, config) => {
175
+ // return {
176
+ // data: "lorem ipsum",
177
+ // }
178
+ // }
179
+ // }
180
+
181
+ // /** @param {string} sourcePath */
182
+ // function router({ base, ...parsed }) {
183
+ // return { ...parsed, ext: ".html" }
184
+ // }
185
+
186
+ // /** @type {VotivePlugin} */
187
+ // const examplePlugin = {
188
+ // name: "example plugin",
189
+ // runners: {
190
+ // exampleRunner: exampleRunner
191
+ // },
192
+ // router,
193
+ // processors: [exampleProcessor]
194
+ // }
195
+
196
+ // /** @type {Runner} */
197
+ // async function exampleRunner(data, database) {
198
+ // return await new Promise((resolve) => setTimeout(() => resolve(data), 1))
199
+ // }
200
+
201
+ // /** @type {VotiveConfig} */
202
+ // const config = {
203
+ // sourceFolder: "./markdown",
204
+ // destinationFolder: temp.path,
205
+ // plugins: [examplePlugin]
206
+ // }
207
+
208
+ // /** @type {FlatProcessors} */
209
+ // const processors = [
210
+ // {
211
+ // plugin: examplePlugin,
212
+ // processor: exampleProcessor
213
+ // }
214
+ // ]
215
+
216
+ // try {
217
+ // fs.writeFileSync("markdown/prunee.md", "A little content")
218
+
219
+ // const database = votive.createDatabase()
220
+ // const sourcesOne = database.getAllSources()
221
+
222
+ // t.test("no sources on startup", () => {
223
+ // const isArray = Array.isArray(sourcesOne)
224
+ // const isEmpty = !sourcesOne.length
225
+ // assert(isArray && isEmpty)
226
+ // })
227
+
228
+
229
+
230
+ // const stop = stopwatch("Read sources")
231
+
232
+ // const { folders, sources } = await votive.readSources(config, database, processors)
233
+
234
+ // t.test('one folder exists', () => {
235
+ // assert(folders.length === 1)
236
+ // })
237
+
238
+ // t.test('three sources exist', () => {
239
+ // assert(sources.length === 4)
240
+ // })
241
+
242
+ // stop()
243
+
244
+ // const sourcesJobs = sources.flatMap(source => source.jobs)
207
245
 
208
- database.createOrUpdateDestination({ metadata: { a: 3, b: 4 }, path: "abc/def.html", abstract: { c: 5 }, syntax: "md" })
209
- const destination = database.getDestinationDependently("abc.html", "abc/def.html")
246
+ // const { processedAbstracts, abstractsJobs } = votive.readAbstracts(sources, config, database, processors)
247
+
248
+ // t.test('transformation succeeded', () => {
249
+ // assert(processedAbstracts.find(abstract => abstract.exampleAppend))
250
+ // })
210
251
 
211
- console.info(`'abc/def.html' requests the property 'a' from 'abc.html', creating a dependency: ${destination.metadata.a}`)
252
+ // t.test('abstract jobs exist', () => {
253
+ // assert(abstractsJobs.length > 0)
254
+ // assert(abstractsJobs.every(job => job.data && job.runner))
255
+ // })
212
256
 
213
- const dependencies = database.getDependencies()
257
+ // const foldersJobs = readFolders(folders, config, database, processors)
214
258
 
215
- t.test('dependency created', () => {
216
- assert(dependencies[0].dependent === 'abc/def.html')
217
- })
259
+ // t.test("folders jobs exist", () => {
260
+ // assert(foldersJobs.length > 0)
261
+ // assert(foldersJobs.every(job => job.data && job.runner))
262
+ // })
218
263
 
264
+ // database.createOrUpdateDestination({ metadata: { a: 1, b: 2 }, path: "abc.html", abstract: { c: 3 }, syntax: "md" })
265
+ // const firstDestination = database.getDestinationIndependently("abc.html", [])
219
266
 
220
- const setting = database.setSetting("abc.html", "theme", "blue", "markdown/prunee.md")
221
- const newSetting = database.setSetting("abc", "category", "Dog", "markdown/prunee.md")
222
- const sameSetting = database.setSetting("abc", "category", "Dog", "markdown/prunee.md")
223
- const folderSetting = database.setSetting("abc", "theme", "red", "markdown/prunee.md")
267
+ // t.test('first destination created', () => {
268
+ // assert(firstDestination.path === 'abc.html')
269
+ // })
224
270
 
225
- t.test('settings created', () => {
226
- assert(setting.value === 'blue')
227
- assert(newSetting.value === 'Dog')
228
- assert(!sameSetting)
229
- })
271
+ // database.createOrUpdateDestination({ metadata: { a: 3, b: 4 }, path: "abc/def.html", abstract: { c: 5 }, syntax: "md" })
230
272
 
231
- const abcSettings = database.getSettings("abc.html")
273
+ // /* TODO: Test that dependencies are working */
232
274
 
233
- t.test('settings retrieved', () => {
234
- assert(abcSettings.theme[0] === "blue")
235
- })
275
+ // const setting = database.setSetting("abc.html", "theme", "blue", "markdown/prunee.md")
276
+ // const newSetting = database.setSetting("abc", "category", "Dog", "markdown/prunee.md")
277
+ // const sameSetting = database.setSetting("abc", "category", "Dog", "markdown/prunee.md")
278
+ // const folderSetting = database.setSetting("abc", "theme", "red", "markdown/prunee.md")
236
279
 
237
- const descendentSetting = database.setSetting("abc/def.html", "theme", "green", "abc.md")
238
- const defSettings = database.getSettings("abc/def.html")
280
+ // t.test('settings created', () => {
281
+ // assert(setting.value === 'blue')
282
+ // assert(newSetting.value === 'Dog')
283
+ // assert(!sameSetting)
284
+ // })
239
285
 
240
- t.test('settings retrieved', () => {
241
- assert(abcSettings.theme[0] === "blue")
242
- assert(defSettings.theme[1] === "green")
243
- })
286
+ // const abcSettings = database.getSettings("abc.html")
244
287
 
245
- const staleDestinations = database.getStaleDestinations()
288
+ // t.test('settings retrieved', () => {
289
+ // assert(abcSettings.theme[0] === "blue")
290
+ // })
246
291
 
247
- t.test('all destinations are stale', () => {
248
- assert(staleDestinations.length === 7)
249
- })
292
+ // const descendentSetting = database.setSetting("abc/def.html", "theme", "green", "abc.md")
293
+ // const defSettings = database.getSettings("abc/def.html")
250
294
 
251
- const written = await votive.writeDestinations(config, database)
295
+ // t.test('settings retrieved', () => {
296
+ // assert(abcSettings.theme[0] === "blue")
297
+ // assert(defSettings.theme[1] === "green")
298
+ // })
252
299
 
253
- const staleDestinationsAfterWriting = database.getStaleDestinations()
300
+ // const staleDestinations = database.getStaleDestinations()
254
301
 
255
- t.test('all destinations are fresh', () => {
256
- assert(staleDestinationsAfterWriting.length === 0)
257
- })
302
+ // t.test('all destinations are stale', () => {
303
+ // assert(staleDestinations.length === 7)
304
+ // })
258
305
 
259
- const updated = database.createOrUpdateDestination({ metadata: { a: 1, b: 9 }, path: "abc.html", abstract: { c: 4 }, syntax: "md" })
260
- const staleAfterAbstractUpdate = database.getStaleDestinations()
306
+ // const written = await votive.writeDestinations(config, database)
261
307
 
262
- t.test('updated document with no side effects is stale', () => {
263
- assert(staleAfterAbstractUpdate.length === 1)
264
- })
308
+ // const staleDestinationsAfterWriting = database.getStaleDestinations()
265
309
 
266
- await votive.writeDestinations(config, database)
267
- const staleAfterSecondWrite = database.getStaleDestinations()
310
+ // t.test('all destinations are fresh', () => {
311
+ // assert(staleDestinationsAfterWriting.length === 0)
312
+ // })
268
313
 
269
- t.test('everything fresh again', () => {
270
- assert(staleAfterSecondWrite.length === 0)
271
- })
314
+ // const updated = database.createOrUpdateDestination({ metadata: { a: 1, b: 9 }, path: "abc.html", abstract: { c: 4 }, syntax: "md" })
315
+ // const staleAfterAbstractUpdate = database.getStaleDestinations()
272
316
 
273
- const updatedWithSideEffects = database.createOrUpdateDestination({ metadata: { a: 4, b: 9 }, path: "abc.html", abstract: { c: 4 }, syntax: "md" })
274
- const staleAfterSideEffects = database.getStaleDestinations()
317
+ // t.test('updated document with no side effects is stale', () => {
318
+ // assert(staleAfterAbstractUpdate.length === 1)
319
+ // })
275
320
 
276
- t.test('side effects work', () => {
277
- assert(staleAfterSideEffects.length === 2)
278
- })
321
+ // await votive.writeDestinations(config, database)
322
+ // const staleAfterSecondWrite = database.getStaleDestinations()
279
323
 
280
- const freshDestinations = database.getAllDestinations()
324
+ // t.test('everything fresh again', () => {
325
+ // assert(staleAfterSecondWrite.length === 0)
326
+ // })
281
327
 
282
- await votive.writeDestinations(config, database)
328
+ // const updatedWithSideEffects = database.createOrUpdateDestination({ metadata: { a: 4, b: 9 }, path: "abc.html", abstract: { c: 4 }, syntax: "md" })
329
+ // const staleAfterSideEffects = database.getStaleDestinations()
283
330
 
284
- const oldSetting = database.setSetting("abc", "theme", "green")
331
+ // t.test('side effects work', () => {
332
+ // assert(staleAfterSideEffects.length === 2)
333
+ // })
285
334
 
286
- const staleAfterSettingsChange = database.getStaleDestinations()
287
335
 
288
- t.test('setting affects descendents', () => {
289
- assert(staleAfterSettingsChange.length === 1)
290
- })
336
+ // await votive.writeDestinations(config, database)
291
337
 
292
- // TODO: Write tests for jobs
293
- const jobs = [...sourcesJobs, ...abstractsJobs, ...foldersJobs]
294
- runJobs(jobs, config, database)
338
+ // const oldSetting = database.setSetting("abc", "theme", "green")
295
339
 
296
- const destinations = database.getAllDestinations()
297
- const prunee = database.getDestinationDependently("markdown/prunee.html", "abc/def.html")
340
+ // const staleAfterSettingsChange = database.getStaleDestinations()
298
341
 
299
- console.info(`'abc/def.html' requests the property 'title' from 'markdown/prunee.html', creating a dependency: ${prunee.metadata.title}`)
300
- const newDependencies = database.getDependencies()
342
+ // t.test('setting affects descendents', () => {
343
+ // assert(staleAfterSettingsChange.length === 1)
344
+ // })
301
345
 
302
- fs.rmSync("markdown/prunee.md")
346
+ // // TODO: Write tests for jobs
347
+ // const jobs = [...sourcesJobs, ...abstractsJobs, ...foldersJobs]
348
+ // runJobs(jobs, config, database)
303
349
 
304
- await votive.pruneSources(config, database)
350
+ // fs.rmSync("markdown/prunee.md")
305
351
 
306
- const result = database.getDestinationIndependently("markdown/prunee.html")
307
- const prunedDependencies = database.getDependencies()
308
- const prunedMetadata = database.getAllMetadataByPath("markdown/prunee.html")
352
+ // await votive.pruneSources(config, database)
309
353
 
310
- t.test('source pruned', () => {
311
- assert(!result)
312
- assert(newDependencies.length - prunedDependencies.length === 1)
313
- assert(!prunedMetadata)
314
- })
354
+ // const result = database.getDestinationIndependently("markdown/prunee.html")
315
355
 
316
- const finalDestinations = database.getDestinations({
317
- filter: [
318
- {
319
- property: "a",
320
- operator: "gt",
321
- value: 3
322
- }
323
- ]
324
- }, "markdown/prunee.html")
356
+ // /* TODO: Check pruning metadata works (I removed it) */
325
357
 
326
- const finalDependencies = database.getDependencies()
358
+ // const finalDestinations = database.getDestinations({
359
+ // filter: [
360
+ // {
361
+ // property: "a",
362
+ // operator: "gt",
363
+ // value: 3
364
+ // }
365
+ // ]
366
+ // }, "markdown/prunee.html")
327
367
 
328
- // TODO: Write a test for get destinations
368
+ // // TODO: Write a test for get destinations
329
369
 
330
- await database.saveDB()
370
+ // await database.saveDB()
331
371
 
332
- temp.remove()
333
- } catch (error) {
334
- temp.remove()
335
- throw error
336
- }
337
- })
372
+ // temp.remove()
373
+ // } catch (error) {
374
+ // temp.remove()
375
+ // throw error
376
+ // }
377
+ // })
@@ -0,0 +1 @@
1
+ A little content
package/tests/.votive.db DELETED
Binary file