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