votive 0.2.1 → 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 -118
- 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 -168
- 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,56 +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
|
-
|
|
32
|
+
const sources = readingSourceFiles && await Promise.all(readingSourceFiles)
|
|
43
33
|
return {
|
|
44
34
|
folders,
|
|
45
|
-
sources
|
|
35
|
+
sources
|
|
46
36
|
}
|
|
47
37
|
}
|
|
48
38
|
|
|
49
39
|
/**
|
|
50
40
|
* @param {VotiveConfig} config
|
|
51
41
|
* @param {Database} database
|
|
52
|
-
* @param {Dirent[]} dirents
|
|
53
|
-
*/
|
|
54
|
-
async function pruneDeletions(config, database, dirents) {
|
|
55
|
-
const sourceFilePaths = new Set(dirents.map(d => d.isFile() && path.join(d.parentPath, d.name)))
|
|
56
|
-
const sourceRecords = database.source.getAll()
|
|
57
|
-
const sourceRecordPaths = new Set(sourceRecords.map(r => r.path))
|
|
58
|
-
|
|
59
|
-
const deletions = sourceRecordPaths.difference(sourceFilePaths)
|
|
60
|
-
|
|
61
|
-
let deletedSources = []
|
|
62
|
-
|
|
63
|
-
deletions.forEach(deletion => {
|
|
64
|
-
deletedSources.push(database.source.delete(deletion))
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
return deletedSources.filter(a => a)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* @param {VotiveConfig} config
|
|
72
42
|
*/
|
|
73
|
-
function fileFilter(config) {
|
|
43
|
+
function fileFilter(config, database) {
|
|
74
44
|
/** @param {Dirent} dirent */
|
|
75
45
|
return (dirent) => {
|
|
76
|
-
const isDestinationFolder = !path.relative(config.destinationFolder, path.join(dirent.parentPath, dirent.name))
|
|
77
|
-
|
|
78
46
|
if (dirent.parentPath === config.destinationFolder) {
|
|
79
47
|
return false
|
|
80
48
|
} else if (dirent.parentPath.startsWith(config.destinationFolder + path.sep)) {
|
|
@@ -83,28 +51,21 @@ function fileFilter(config) {
|
|
|
83
51
|
return false // Ignore hidden files
|
|
84
52
|
} else if (dirent.parentPath.includes(path.sep + ".")) {
|
|
85
53
|
return false // Ignore hidden folders
|
|
86
|
-
} else if (dirent.parentPath.match(/^\.\w/)) {
|
|
87
|
-
return false // Ignore hidden folders
|
|
88
|
-
} if (isDestinationFolder) {
|
|
89
|
-
return false
|
|
90
54
|
}
|
|
91
55
|
return true
|
|
92
56
|
}
|
|
93
57
|
}
|
|
94
58
|
|
|
95
59
|
/**
|
|
96
|
-
* @typedef {
|
|
60
|
+
* @typedef {ReadSourceFilePlugin[]} ReadSourceFileResult
|
|
97
61
|
*/
|
|
98
62
|
|
|
99
63
|
/**
|
|
100
|
-
* @typedef {object}
|
|
64
|
+
* @typedef {object} ReadSourceFilePlugin
|
|
101
65
|
* @property {Abstract} abstract
|
|
102
66
|
* @property {ProcessorSyntax} syntax
|
|
103
|
-
* @property {object} [metadata]
|
|
104
|
-
* @property {string} [targetFilePath]
|
|
105
|
-
* @property {string} [dir]
|
|
106
67
|
* @property {Jobs} jobs
|
|
107
|
-
* @property {string}
|
|
68
|
+
* @property {string} destinationPath
|
|
108
69
|
*/
|
|
109
70
|
|
|
110
71
|
/**
|
|
@@ -115,12 +76,12 @@ function fileFilter(config) {
|
|
|
115
76
|
function readSourceFile(processors, database, config) {
|
|
116
77
|
/**
|
|
117
78
|
* @param {import("node:fs").Dirent} dirent
|
|
118
|
-
* @returns {Promise<
|
|
79
|
+
* @returns {Promise<ReadSourceFilePlugin>[]}
|
|
119
80
|
*/
|
|
120
81
|
return (dirent) => {
|
|
121
82
|
const { name, parentPath } = dirent
|
|
122
|
-
const
|
|
123
|
-
const
|
|
83
|
+
const filePath = path.join(parentPath, name)
|
|
84
|
+
const fileInfo = path.parse(filePath)
|
|
124
85
|
|
|
125
86
|
const processing = processors.flatMap(({ plugin, processor }) => process(plugin, processor, config))
|
|
126
87
|
|
|
@@ -130,98 +91,43 @@ function readSourceFile(processors, database, config) {
|
|
|
130
91
|
* @param {VotiveConfig} config
|
|
131
92
|
*/
|
|
132
93
|
async function process(plugin, processor, config) {
|
|
133
|
-
const {
|
|
134
|
-
if (filter.includes(
|
|
94
|
+
const { read, filter, syntax } = processor
|
|
95
|
+
if (filter.extensions.includes(fileInfo.ext) && read && read.text) {
|
|
135
96
|
|
|
136
97
|
// Check modified time
|
|
137
|
-
const stat = await fs.stat(
|
|
138
|
-
const source = database.
|
|
139
|
-
const diff = source && source.lastModified - Number(Math.floor(stat.mtimeMs))
|
|
140
|
-
|
|
141
|
-
if (source && diff > -1) {
|
|
142
|
-
return null
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
database.setting.deleteBySource(sourceFilePath)
|
|
146
|
-
|
|
147
|
-
const targetFilePath = route(sourceFilePath, plugin, config)
|
|
148
|
-
const targetFileExtension = path.extname(targetFilePath)
|
|
149
|
-
|
|
150
|
-
// Set URL if exists
|
|
151
|
-
const allJobs = []
|
|
152
|
-
|
|
153
|
-
if (format === "buffer") {
|
|
154
|
-
|
|
155
|
-
// FIXME update this type
|
|
156
|
-
const data = read(sourceFilePath, database, config)
|
|
157
|
-
const { metadata, abstract, jobs } = data
|
|
98
|
+
const stat = await fs.stat(filePath)
|
|
99
|
+
const source = database.getSource(filePath)
|
|
158
100
|
|
|
159
|
-
const target = database.target.create({
|
|
160
|
-
metadata,
|
|
161
|
-
abstract,
|
|
162
|
-
path: targetFilePath,
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if (Array.isArray(jobs)) {
|
|
167
|
-
allJobs.push(...jobs)
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
101
|
|
|
171
|
-
if (
|
|
172
|
-
const stats = await fs.stat(sourceFilePath)
|
|
173
|
-
const data = await fs.readFile(sourceFilePath, { encoding: "utf-8" })
|
|
102
|
+
if (source && source.lastModified === Number(stat.mtimeMs.toFixed())) return { jobs: [] }
|
|
174
103
|
|
|
175
|
-
|
|
104
|
+
database.deleteSettings(filePath)
|
|
176
105
|
|
|
106
|
+
// Get destination route
|
|
107
|
+
const destinationPath = route(filePath, plugin)
|
|
177
108
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
abstract,
|
|
184
|
-
path: targetFilePath,
|
|
185
|
-
metadata,
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
// FIXME this won't work with the refactor
|
|
189
|
-
allJobs && allJobs.forEach(job => job.syntax = processor.extensions[0])
|
|
190
|
-
updateSource()
|
|
191
|
-
return {
|
|
192
|
-
abstract,
|
|
193
|
-
metadata,
|
|
194
|
-
syntax: targetFileExtension,
|
|
195
|
-
targetFilePath: target.path,
|
|
196
|
-
dir: target.dir,
|
|
197
|
-
sourceFilePath,
|
|
198
|
-
jobs: allJobs
|
|
199
|
-
}
|
|
200
|
-
}
|
|
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)
|
|
201
114
|
|
|
202
|
-
|
|
203
|
-
allJobs && allJobs.forEach(job => job.syntax = processor.extensions[0])
|
|
204
|
-
updateSource()
|
|
115
|
+
const timeStamp = stat.mtimeMs.toFixed()
|
|
205
116
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
sourceFilePath: null,
|
|
211
|
-
metadata: null,
|
|
212
|
-
syntax: null
|
|
117
|
+
if(source) {
|
|
118
|
+
database.updateSource(filePath, Number(timeStamp))
|
|
119
|
+
} else {
|
|
120
|
+
database.createSource(filePath, destinationPath, Number(timeStamp))
|
|
213
121
|
}
|
|
214
122
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
database.source.create(sourceFilePath, targetFilePath, Number(timeStamp))
|
|
222
|
-
}
|
|
223
|
-
}
|
|
123
|
+
database.createOrUpdateDestination({
|
|
124
|
+
metadata,
|
|
125
|
+
path: destinationPath,
|
|
126
|
+
abstract: abstract,
|
|
127
|
+
syntax: syntax
|
|
128
|
+
})
|
|
224
129
|
|
|
130
|
+
return { abstract, destinationPath, syntax, jobs }
|
|
225
131
|
}
|
|
226
132
|
}
|
|
227
133
|
|
|
@@ -232,43 +138,10 @@ function readSourceFile(processors, database, config) {
|
|
|
232
138
|
/**
|
|
233
139
|
* @param {string} filePath
|
|
234
140
|
* @param {VotivePlugin} plugin
|
|
235
|
-
* @param {VotiveConfig} config
|
|
236
141
|
*/
|
|
237
|
-
function route(filePath, plugin
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const segments = ["", ...dir.split(path.sep).filter(a => a)]
|
|
241
|
-
const pathInfo = {
|
|
242
|
-
inRootDir: rooty,
|
|
243
|
-
dir: segments,
|
|
244
|
-
...parsedPath
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if (!plugin.router) return "0"
|
|
248
|
-
|
|
249
|
-
const routedPath = plugin.router(pathInfo)
|
|
250
|
-
|
|
251
|
-
if (!routedPath) return "0"
|
|
252
|
-
|
|
253
|
-
if (routedPath.hasOwnProperty("dir") && Array.isArray(routedPath.dir)) {
|
|
254
|
-
return path.normalize(path.format({
|
|
255
|
-
dir: path.join(...routedPath.dir),
|
|
256
|
-
root: routedPath.root || "",
|
|
257
|
-
base: routedPath.base,
|
|
258
|
-
name: routedPath.name,
|
|
259
|
-
ext: routedPath.ext
|
|
260
|
-
}))
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
const routedInfo = {
|
|
264
|
-
dir: routedPath.dir || "",
|
|
265
|
-
root: routedPath.root || "",
|
|
266
|
-
base: routedPath.base,
|
|
267
|
-
name: routedPath.name,
|
|
268
|
-
ext: routedPath.ext
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
return path.normalize(path.format(routedInfo))
|
|
142
|
+
function route(filePath, plugin) {
|
|
143
|
+
if (!plugin.router) return ""
|
|
144
|
+
return plugin.router(filePath) || ""
|
|
272
145
|
}
|
|
273
146
|
|
|
274
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"
|