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/README.md +4 -11
- package/lib/bundle.js +26 -119
- package/lib/cleanDeletions.js +32 -0
- package/lib/createDatabase.js +373 -931
- package/lib/readAbstracts.js +9 -12
- package/lib/readFolders.js +15 -18
- package/lib/readSources.js +41 -176
- package/lib/runJobs.js +8 -26
- package/lib/utils/index.js +5 -13
- package/lib/writeDestinations.js +11 -31
- package/package.json +1 -2
- package/tests/index.js +245 -302
package/lib/readAbstracts.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** @import {VotiveConfig, FlatProcessors, Abstract, AbstractsWithSyntax, Jobs, FlatProcessor, Abstracts} from "./bundle.js" */
|
|
2
2
|
/** @import {Database} from "./createDatabase.js" */
|
|
3
|
-
/** @import {
|
|
3
|
+
/** @import {ReadSourceFileResult} from "./readSources.js"
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @typedef {object} ReadAbstractsResult
|
|
@@ -13,17 +13,15 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
-
* @param {
|
|
16
|
+
* @param {ReadSourceFileResult} abstracts
|
|
17
17
|
* @param {VotiveConfig} config
|
|
18
18
|
* @param {Database} database
|
|
19
19
|
* @param {FlatProcessors} processors
|
|
20
20
|
* @returns {{processedAbstracts: Abstracts, abstractsJobs: Jobs}}
|
|
21
21
|
*/
|
|
22
|
-
function readAbstracts(
|
|
22
|
+
function readAbstracts(abstracts, config, database, processors) {
|
|
23
23
|
|
|
24
|
-
const processed =
|
|
25
|
-
|
|
26
|
-
const { abstract: unprocessedAbstract } = file
|
|
24
|
+
const processed = abstracts.flatMap(({ abstract: unprocessedAbstract, syntax }) => {
|
|
27
25
|
|
|
28
26
|
/**
|
|
29
27
|
* @param {FlatProcessors} processors
|
|
@@ -34,14 +32,13 @@ function readAbstracts(files, config, database, processors) {
|
|
|
34
32
|
const [flatProcessor, ...rest] = processors
|
|
35
33
|
if (!flatProcessor) return { abstract, jobs }
|
|
36
34
|
const { processor, plugin } = flatProcessor
|
|
37
|
-
if (
|
|
38
|
-
|| !processor.
|
|
35
|
+
if (processor.syntax !== syntax
|
|
36
|
+
|| !processor.read
|
|
37
|
+
|| !processor.read.abstract
|
|
39
38
|
) return recursiveProcess(rest, abstract, jobs)
|
|
40
39
|
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
const processed = processor.transformFile(abstract, database, config)
|
|
44
|
-
processed.jobs && processed.jobs.forEach(job => job.syntax = file.syntax)
|
|
40
|
+
const processed = processor.read.abstract(abstract, database, config)
|
|
41
|
+
processed.jobs && processed.jobs.forEach(job => job.plugin = plugin.name)
|
|
45
42
|
return recursiveProcess(rest, processed.abstract, [...jobs, ...(processed.jobs || [])])
|
|
46
43
|
}
|
|
47
44
|
|
package/lib/readFolders.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import path from "node:path"
|
|
2
|
-
|
|
3
1
|
/** @import {VotiveConfig, FlatProcessors} from "./bundle.js" */
|
|
4
2
|
/** @import {Database} from "./createDatabase.js" */
|
|
5
3
|
/** @import {Dirent} from "node:fs" */
|
|
6
4
|
|
|
5
|
+
import path from "node:path"
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* @param {Dirent[]} folders
|
|
@@ -13,35 +12,33 @@ import path from "node:path"
|
|
|
13
12
|
*/
|
|
14
13
|
function readFolders(folders = [], config, database, processors) {
|
|
15
14
|
|
|
16
|
-
const folderProcessors = processors.filter(({ processor }) => processor.
|
|
15
|
+
const folderProcessors = processors.filter(({ processor }) => processor.read && processor.read.folder)
|
|
17
16
|
|
|
18
17
|
const processed = folderProcessors.flatMap(({ processor, plugin }) => {
|
|
19
18
|
|
|
20
19
|
const jobs = folders.flatMap(folder => {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const {
|
|
25
|
-
jobs.forEach(job => job.
|
|
26
|
-
if (
|
|
27
|
-
|
|
28
|
-
database.
|
|
20
|
+
const folderPath = path.relative(config.sourceFolder, path.join(folder.parentPath, folder.name))
|
|
21
|
+
|
|
22
|
+
/** @ts-ignore `.read` is throwing a warning, but it's guarded above */
|
|
23
|
+
const { destinations, jobs } = processor.read.folder(folderPath, database, config)
|
|
24
|
+
jobs.forEach(job => job.plugin = plugin.name)
|
|
25
|
+
if (destinations) {
|
|
26
|
+
destinations.forEach(destination => {
|
|
27
|
+
database.createOrUpdateDestination(destination)
|
|
29
28
|
})
|
|
30
29
|
return jobs
|
|
31
30
|
}
|
|
32
31
|
})
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
const rootPath = path.relative(config.sourceFolder, "")
|
|
36
|
-
const rootFolder = processor.readFolder(rootPath, database, config, true)
|
|
33
|
+
const rootFolder = processor.read.folder("", database, config)
|
|
37
34
|
|
|
38
|
-
if (rootFolder.
|
|
39
|
-
rootFolder.
|
|
40
|
-
database.
|
|
35
|
+
if (rootFolder.destinations) {
|
|
36
|
+
rootFolder.destinations.forEach(destination => {
|
|
37
|
+
database.createOrUpdateDestination(destination)
|
|
41
38
|
})
|
|
42
39
|
}
|
|
43
40
|
|
|
44
|
-
rootFolder.jobs.forEach(job => job.
|
|
41
|
+
rootFolder.jobs.forEach(job => job.plugin = plugin.name)
|
|
45
42
|
|
|
46
43
|
if(rootFolder.jobs) jobs.push(... rootFolder.jobs)
|
|
47
44
|
return jobs
|
package/lib/readSources.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { decodeBuffer } from "encoding-sniffer"
|
|
2
2
|
import fs from "node:fs/promises"
|
|
3
3
|
import path from "node:path"
|
|
4
|
-
import
|
|
4
|
+
import { visit } from "unist-util-visit"
|
|
5
5
|
|
|
6
|
-
/** @import {VotiveConfig, VotivePlugin, VotiveProcessor, FlatProcessors, Abstracts, Abstract, Jobs, ProcessorSyntax
|
|
6
|
+
/** @import {VotiveConfig, VotivePlugin, VotiveProcessor, FlatProcessors, Abstracts, Abstract, Jobs, ProcessorSyntax} from "./bundle.js" */
|
|
7
7
|
/** @import {Dirent} from "node:fs" */
|
|
8
8
|
/** @import {Database} from "./createDatabase.js" */
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* @typedef {object} ReadSourcesResult
|
|
12
12
|
* @property {Dirent[]} folders
|
|
13
|
-
* @property {
|
|
13
|
+
* @property {ReadSourceFilePlugin[]} sources
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -25,64 +25,24 @@ async function readSources(config, database, processors) {
|
|
|
25
25
|
recursive: true
|
|
26
26
|
})
|
|
27
27
|
|
|
28
|
-
const filteredDirents = (dirents || []).filter(fileFilter(config))
|
|
29
|
-
|
|
28
|
+
const filteredDirents = (dirents || []).filter(fileFilter(config, database))
|
|
30
29
|
const { files, folders } = Object.groupBy(filteredDirents, (dirent) => dirent.isFile() ? "files" : "folders")
|
|
31
|
-
const loadingFiles = files.map(a => path.normalize(path.format({ name: a.name, dir: a.parentPath })))
|
|
32
30
|
if (!files) return { folders, sources: [] }
|
|
33
|
-
const limit = pLimit(5)
|
|
34
31
|
const readingSourceFiles = files.flatMap(readSourceFile(processors, database, config))
|
|
35
|
-
|
|
36
|
-
const reading = [
|
|
37
|
-
(await Promise.all(readingSourceFiles)).filter(a => a),
|
|
38
|
-
pruneDeletions(config, database, filteredDirents)
|
|
39
|
-
]
|
|
40
|
-
|
|
41
|
-
const [sources, deletedSources] = await Promise.all(reading)
|
|
42
|
-
|
|
43
|
-
// const sources = readingSourceFiles && (await Promise.all(readingSourceFiles)).filter(a => a)
|
|
44
|
-
|
|
45
|
-
console.log({ sources })
|
|
46
|
-
|
|
47
|
-
// const deletedSources = await pruneDeletions(config, database, filteredDirents)
|
|
48
|
-
|
|
49
|
-
console.log({ deletedSources })
|
|
50
|
-
|
|
32
|
+
const sources = readingSourceFiles && await Promise.all(readingSourceFiles)
|
|
51
33
|
return {
|
|
52
34
|
folders,
|
|
53
|
-
sources
|
|
35
|
+
sources
|
|
54
36
|
}
|
|
55
37
|
}
|
|
56
38
|
|
|
57
39
|
/**
|
|
58
40
|
* @param {VotiveConfig} config
|
|
59
41
|
* @param {Database} database
|
|
60
|
-
* @param {Dirent[]} dirents
|
|
61
|
-
*/
|
|
62
|
-
async function pruneDeletions(config, database, dirents) {
|
|
63
|
-
const sourceFilePaths = new Set(dirents.map(d => d.isFile() && path.join(d.parentPath, d.name)))
|
|
64
|
-
const sourceRecords = database.source.getAll()
|
|
65
|
-
const sourceRecordPaths = new Set(sourceRecords.map(r => r.path))
|
|
66
|
-
|
|
67
|
-
const deletions = sourceRecordPaths.difference(sourceFilePaths)
|
|
68
|
-
|
|
69
|
-
let deletedSources = []
|
|
70
|
-
|
|
71
|
-
deletions.forEach(deletion => {
|
|
72
|
-
deletedSources.push(database.source.delete(deletion))
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
return deletedSources.filter(a => a)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* @param {VotiveConfig} config
|
|
80
42
|
*/
|
|
81
|
-
function fileFilter(config) {
|
|
43
|
+
function fileFilter(config, database) {
|
|
82
44
|
/** @param {Dirent} dirent */
|
|
83
45
|
return (dirent) => {
|
|
84
|
-
const isDestinationFolder = !path.relative(config.destinationFolder, path.join(dirent.parentPath, dirent.name))
|
|
85
|
-
|
|
86
46
|
if (dirent.parentPath === config.destinationFolder) {
|
|
87
47
|
return false
|
|
88
48
|
} else if (dirent.parentPath.startsWith(config.destinationFolder + path.sep)) {
|
|
@@ -91,28 +51,21 @@ function fileFilter(config) {
|
|
|
91
51
|
return false // Ignore hidden files
|
|
92
52
|
} else if (dirent.parentPath.includes(path.sep + ".")) {
|
|
93
53
|
return false // Ignore hidden folders
|
|
94
|
-
} else if (dirent.parentPath.match(/^\.\w/)) {
|
|
95
|
-
return false // Ignore hidden folders
|
|
96
|
-
} if (isDestinationFolder) {
|
|
97
|
-
return false
|
|
98
54
|
}
|
|
99
55
|
return true
|
|
100
56
|
}
|
|
101
57
|
}
|
|
102
58
|
|
|
103
59
|
/**
|
|
104
|
-
* @typedef {
|
|
60
|
+
* @typedef {ReadSourceFilePlugin[]} ReadSourceFileResult
|
|
105
61
|
*/
|
|
106
62
|
|
|
107
63
|
/**
|
|
108
|
-
* @typedef {object}
|
|
64
|
+
* @typedef {object} ReadSourceFilePlugin
|
|
109
65
|
* @property {Abstract} abstract
|
|
110
66
|
* @property {ProcessorSyntax} syntax
|
|
111
|
-
* @property {object} [metadata]
|
|
112
|
-
* @property {string} [targetFilePath]
|
|
113
|
-
* @property {string} [dir]
|
|
114
67
|
* @property {Jobs} jobs
|
|
115
|
-
* @property {string}
|
|
68
|
+
* @property {string} destinationPath
|
|
116
69
|
*/
|
|
117
70
|
|
|
118
71
|
/**
|
|
@@ -123,12 +76,12 @@ function fileFilter(config) {
|
|
|
123
76
|
function readSourceFile(processors, database, config) {
|
|
124
77
|
/**
|
|
125
78
|
* @param {import("node:fs").Dirent} dirent
|
|
126
|
-
* @returns {Promise<
|
|
79
|
+
* @returns {Promise<ReadSourceFilePlugin>[]}
|
|
127
80
|
*/
|
|
128
81
|
return (dirent) => {
|
|
129
82
|
const { name, parentPath } = dirent
|
|
130
|
-
const
|
|
131
|
-
const
|
|
83
|
+
const filePath = path.join(parentPath, name)
|
|
84
|
+
const fileInfo = path.parse(filePath)
|
|
132
85
|
|
|
133
86
|
const processing = processors.flatMap(({ plugin, processor }) => process(plugin, processor, config))
|
|
134
87
|
|
|
@@ -138,98 +91,43 @@ function readSourceFile(processors, database, config) {
|
|
|
138
91
|
* @param {VotiveConfig} config
|
|
139
92
|
*/
|
|
140
93
|
async function process(plugin, processor, config) {
|
|
141
|
-
const {
|
|
142
|
-
if (filter.includes(
|
|
94
|
+
const { read, filter, syntax } = processor
|
|
95
|
+
if (filter.extensions.includes(fileInfo.ext) && read && read.text) {
|
|
143
96
|
|
|
144
97
|
// Check modified time
|
|
145
|
-
const stat = await fs.stat(
|
|
146
|
-
const source = database.
|
|
147
|
-
const diff = source && source.lastModified - Number(Math.floor(stat.mtimeMs))
|
|
148
|
-
|
|
149
|
-
if (source && diff > -1) {
|
|
150
|
-
return null
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
database.setting.deleteBySource(sourceFilePath)
|
|
98
|
+
const stat = await fs.stat(filePath)
|
|
99
|
+
const source = database.getSource(filePath)
|
|
154
100
|
|
|
155
|
-
const targetFilePath = route(sourceFilePath, plugin, config)
|
|
156
|
-
const targetFileExtension = path.extname(targetFilePath)
|
|
157
101
|
|
|
158
|
-
|
|
159
|
-
const allJobs = []
|
|
160
|
-
|
|
161
|
-
if (format === "buffer") {
|
|
162
|
-
|
|
163
|
-
// FIXME update this type
|
|
164
|
-
const data = read(sourceFilePath, database, config)
|
|
165
|
-
const { metadata, abstract, jobs } = data
|
|
166
|
-
|
|
167
|
-
const target = database.target.create({
|
|
168
|
-
metadata,
|
|
169
|
-
abstract,
|
|
170
|
-
path: targetFilePath,
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (Array.isArray(jobs)) {
|
|
175
|
-
allJobs.push(...jobs)
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (format === "text") {
|
|
180
|
-
const stats = await fs.stat(sourceFilePath)
|
|
181
|
-
const data = await fs.readFile(sourceFilePath, { encoding: "utf-8" })
|
|
182
|
-
|
|
183
|
-
const { jobs, abstract, metadata } = read(data, sourceFilePath, targetFilePath, database, config)
|
|
102
|
+
if (source && source.lastModified === Number(stat.mtimeMs.toFixed())) return { jobs: [] }
|
|
184
103
|
|
|
104
|
+
database.deleteSettings(filePath)
|
|
185
105
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
106
|
+
// Get destination route
|
|
107
|
+
const destinationPath = route(filePath, plugin)
|
|
189
108
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
// FIXME this won't work with the refactor
|
|
197
|
-
allJobs && allJobs.forEach(job => job.syntax = processor.extensions[0])
|
|
198
|
-
updateSource()
|
|
199
|
-
return {
|
|
200
|
-
abstract,
|
|
201
|
-
metadata,
|
|
202
|
-
syntax: targetFileExtension,
|
|
203
|
-
targetFilePath: target.path,
|
|
204
|
-
dir: target.dir,
|
|
205
|
-
sourceFilePath,
|
|
206
|
-
jobs: allJobs
|
|
207
|
-
}
|
|
208
|
-
}
|
|
109
|
+
// Set URL if exists
|
|
110
|
+
const buffer = await fs.readFile(path.format(fileInfo))
|
|
111
|
+
const data = decodeBuffer(buffer)
|
|
112
|
+
const { jobs, abstract, metadata } = read.text(data, filePath, database, config)
|
|
113
|
+
jobs && jobs.forEach(job => job.plugin = plugin.name)
|
|
209
114
|
|
|
210
|
-
|
|
211
|
-
allJobs && allJobs.forEach(job => job.syntax = processor.extensions[0])
|
|
212
|
-
updateSource()
|
|
115
|
+
const timeStamp = stat.mtimeMs.toFixed()
|
|
213
116
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
sourceFilePath: null,
|
|
219
|
-
metadata: null,
|
|
220
|
-
syntax: null
|
|
117
|
+
if(source) {
|
|
118
|
+
database.updateSource(filePath, Number(timeStamp))
|
|
119
|
+
} else {
|
|
120
|
+
database.createSource(filePath, destinationPath, Number(timeStamp))
|
|
221
121
|
}
|
|
222
122
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
database.source.create(sourceFilePath, targetFilePath, Number(timeStamp))
|
|
230
|
-
}
|
|
231
|
-
}
|
|
123
|
+
database.createOrUpdateDestination({
|
|
124
|
+
metadata,
|
|
125
|
+
path: destinationPath,
|
|
126
|
+
abstract: abstract,
|
|
127
|
+
syntax: syntax
|
|
128
|
+
})
|
|
232
129
|
|
|
130
|
+
return { abstract, destinationPath, syntax, jobs }
|
|
233
131
|
}
|
|
234
132
|
}
|
|
235
133
|
|
|
@@ -240,43 +138,10 @@ function readSourceFile(processors, database, config) {
|
|
|
240
138
|
/**
|
|
241
139
|
* @param {string} filePath
|
|
242
140
|
* @param {VotivePlugin} plugin
|
|
243
|
-
* @param {VotiveConfig} config
|
|
244
141
|
*/
|
|
245
|
-
function route(filePath, plugin
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
const segments = ["", ...dir.split(path.sep).filter(a => a)]
|
|
249
|
-
const pathInfo = {
|
|
250
|
-
inRootDir: rooty,
|
|
251
|
-
dir: segments,
|
|
252
|
-
...parsedPath
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (!plugin.router) return "0"
|
|
256
|
-
|
|
257
|
-
const routedPath = plugin.router(pathInfo)
|
|
258
|
-
|
|
259
|
-
if (!routedPath) return "0"
|
|
260
|
-
|
|
261
|
-
if (routedPath.hasOwnProperty("dir") && Array.isArray(routedPath.dir)) {
|
|
262
|
-
return path.normalize(path.format({
|
|
263
|
-
dir: path.join(...routedPath.dir),
|
|
264
|
-
root: routedPath.root || "",
|
|
265
|
-
base: routedPath.base,
|
|
266
|
-
name: routedPath.name,
|
|
267
|
-
ext: routedPath.ext
|
|
268
|
-
}))
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const routedInfo = {
|
|
272
|
-
dir: routedPath.dir || "",
|
|
273
|
-
root: routedPath.root || "",
|
|
274
|
-
base: routedPath.base,
|
|
275
|
-
name: routedPath.name,
|
|
276
|
-
ext: routedPath.ext
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
return path.normalize(path.format(routedInfo))
|
|
142
|
+
function route(filePath, plugin) {
|
|
143
|
+
if (!plugin.router) return ""
|
|
144
|
+
return plugin.router(filePath) || ""
|
|
280
145
|
}
|
|
281
146
|
|
|
282
147
|
export default readSources
|
package/lib/runJobs.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import workerpool from "workerpool"
|
|
2
|
+
|
|
1
3
|
/** @import {Job, Jobs, Database, VotiveConfig} from "./bundle.js" */
|
|
2
4
|
|
|
5
|
+
const pool = workerpool.pool()
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
8
|
* @param {Jobs} jobs
|
|
@@ -7,35 +10,14 @@
|
|
|
7
10
|
* @param {Database} database
|
|
8
11
|
*/
|
|
9
12
|
async function runJobs(jobs, config, database) {
|
|
10
|
-
const
|
|
11
|
-
const running = jobs.flatMap(async job => {
|
|
13
|
+
const running = jobs.map(async job => {
|
|
12
14
|
if (!job) return
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
const processing = processors.map(async processor => {
|
|
16
|
-
if (processor.syntax === job.syntax) {
|
|
17
|
-
try {
|
|
18
|
-
const response = await fetch(job.data)
|
|
19
|
-
if (response.status >= 200 && response.status < 300) {
|
|
20
|
-
const data = await response[job.runner]()
|
|
21
|
-
const processed = processor.fetcher(data)
|
|
22
|
-
database.createURL(job.data, processed, job.destination)
|
|
23
|
-
return
|
|
24
|
-
} else {
|
|
25
|
-
console.warn(`Error fetching URL: ${job.data}`)
|
|
26
|
-
}
|
|
27
|
-
} catch (e) {
|
|
28
|
-
console.error(e)
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
return Promise.allSettled(processing)
|
|
15
|
+
const plugin = config.plugins.find(plugin => plugin.name === job.plugin)
|
|
16
|
+
return pool.exec(plugin.runners[job.runner], [job.data])
|
|
35
17
|
})
|
|
36
18
|
|
|
37
|
-
const
|
|
38
|
-
|
|
19
|
+
const ran = await Promise.allSettled(running)
|
|
20
|
+
pool.terminate()
|
|
39
21
|
}
|
|
40
22
|
|
|
41
23
|
export default runJobs
|
package/lib/utils/index.js
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
import { styleText } from "node:util"
|
|
2
2
|
import { statSync } from "node:fs"
|
|
3
|
-
import path from "path"
|
|
4
3
|
|
|
5
|
-
/**
|
|
6
|
-
|
|
7
|
-
* @param {string} message
|
|
8
|
-
* @param {boolean} verbose
|
|
9
|
-
*/
|
|
10
|
-
export function stopwatch(label, message, verbose) {
|
|
4
|
+
/** @param {string} label */
|
|
5
|
+
export function stopwatch(label) {
|
|
11
6
|
const start = performance.now()
|
|
12
7
|
|
|
13
8
|
function stop() {
|
|
14
9
|
const end = performance.now()
|
|
15
10
|
const duration = end - start
|
|
16
|
-
|
|
11
|
+
console.info(`${label}: ${styleText("red", duration.toFixed(2) + "ms")}`)
|
|
17
12
|
}
|
|
18
13
|
|
|
19
14
|
return stop
|
|
@@ -23,11 +18,8 @@ export function stopwatch(label, message, verbose) {
|
|
|
23
18
|
* @param {string} urlPath
|
|
24
19
|
*/
|
|
25
20
|
export function splitURL(urlPath) {
|
|
26
|
-
const [urlFileName, ...urlDirSegmentsReversed] = urlPath.split("/").
|
|
27
|
-
|
|
28
|
-
segments.push('')
|
|
29
|
-
const folder = path.relative("", segments.join(path.sep))
|
|
30
|
-
return folder
|
|
21
|
+
const [urlFileName, ...urlDirSegmentsReversed] = urlPath.split("/").reverse()
|
|
22
|
+
return [urlDirSegmentsReversed.reverse().join("/") || "/", urlFileName]
|
|
31
23
|
}
|
|
32
24
|
|
|
33
25
|
/** @param {string} dbPath */
|
package/lib/writeDestinations.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises"
|
|
2
2
|
import path from "node:path"
|
|
3
|
-
import { checkFile } from "./utils/index.js"
|
|
4
3
|
|
|
5
4
|
/** @import {Abstract, Abstracts, VotiveConfig} from "./bundle.js" */
|
|
6
5
|
/** @import {Database} from "./createDatabase.js" */
|
|
@@ -12,46 +11,27 @@ import { checkFile } from "./utils/index.js"
|
|
|
12
11
|
*/
|
|
13
12
|
async function writeDestinations(config, database) {
|
|
14
13
|
const writeProcessors = config && config.plugins && config.plugins.flatMap(plugin => (
|
|
15
|
-
plugin.processors && plugin.processors.map(
|
|
14
|
+
plugin.processors && plugin.processors.map(({ write, syntax }) => write && { write, syntax }).filter(a => a)
|
|
16
15
|
)).filter(a => a)
|
|
17
16
|
|
|
18
17
|
if (!writeProcessors || !writeProcessors.length) throw "No write processor provided"
|
|
19
18
|
|
|
20
|
-
const
|
|
19
|
+
const destinations = database.getStaleDestinations()
|
|
20
|
+
if (!destinations) return
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const writing = targets.filter(({ path }) => path !== "0").flatMap(destination => {
|
|
25
|
-
|
|
26
|
-
/* FIXME the following line doesn't make sense */
|
|
27
|
-
if (!destination.path) database.target.markFresh(destination.path)
|
|
28
|
-
if (!Object.keys(destination.abstract).length) return
|
|
22
|
+
const writing = destinations.flatMap(destination => {
|
|
29
23
|
const destinationPath = path.join(config.destinationFolder, String(destination.path))
|
|
30
24
|
const { dir } = path.parse(destinationPath)
|
|
31
|
-
return writeProcessors.map(
|
|
32
|
-
if (processor.
|
|
33
|
-
const
|
|
34
|
-
if (!writeInfo) return
|
|
35
|
-
const { data, encoding = 'utf-8' } = writeInfo
|
|
25
|
+
return writeProcessors.map(processor => {
|
|
26
|
+
if (processor.syntax === destination.syntax) {
|
|
27
|
+
const { data, encoding = 'utf-8' } = processor.write(destination, database, config)
|
|
36
28
|
|
|
37
29
|
async function write() {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
await mkdir(dir, { recursive: true })
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (data) {
|
|
45
|
-
if (processor.format === "text") {
|
|
46
|
-
await writeFile(destinationPath, data, "utf-8")
|
|
47
|
-
database.target.markFresh(destination.path)
|
|
48
|
-
} else {
|
|
49
|
-
await writeFile(destinationPath, data)
|
|
50
|
-
database.target.markFresh(destination.path)
|
|
51
|
-
}
|
|
52
|
-
}
|
|
30
|
+
await mkdir(dir, { recursive: true })
|
|
31
|
+
await writeFile(destinationPath, data, encoding)
|
|
32
|
+
database.freshenDependency(destination.path)
|
|
53
33
|
}
|
|
54
|
-
|
|
34
|
+
|
|
55
35
|
return write()
|
|
56
36
|
}
|
|
57
37
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "votive",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A file processor.",
|
|
5
5
|
"homepage": "https://github.com/samlfair/votive#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -24,7 +24,6 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"encoding-sniffer": "^0.2.1",
|
|
27
|
-
"p-limit": "^7.3.0",
|
|
28
27
|
"unified": "^11.0.5",
|
|
29
28
|
"unist-util-visit": "^5.0.0",
|
|
30
29
|
"workerpool": "^10.0.1"
|