mikser-io 6.0.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/LICENSE +21 -0
- package/README.md +49 -0
- package/app.js +7 -0
- package/index.js +11 -0
- package/mikser-lockup-stacked.svg +14 -0
- package/package.json +64 -0
- package/src/catalog.js +56 -0
- package/src/config.js +37 -0
- package/src/constants.js +20 -0
- package/src/engine.js +317 -0
- package/src/journal.js +108 -0
- package/src/lifecycle.js +299 -0
- package/src/manager.js +119 -0
- package/src/plugins/api.js +176 -0
- package/src/plugins/assets.js +346 -0
- package/src/plugins/commands.js +75 -0
- package/src/plugins/data.js +138 -0
- package/src/plugins/documents.js +96 -0
- package/src/plugins/files.js +153 -0
- package/src/plugins/front-matter.js +24 -0
- package/src/plugins/json.js +20 -0
- package/src/plugins/layouts.js +368 -0
- package/src/plugins/mapper.js +29 -0
- package/src/plugins/post/pdf.js +60 -0
- package/src/plugins/render/asset.js +11 -0
- package/src/plugins/render/file.js +16 -0
- package/src/plugins/render/hbs.js +52 -0
- package/src/plugins/render/href.js +45 -0
- package/src/plugins/render/preset.js +13 -0
- package/src/plugins/render/resource.js +17 -0
- package/src/plugins/resources.js +216 -0
- package/src/plugins/rest.js +143 -0
- package/src/plugins/shares.js +40 -0
- package/src/plugins/validator.js +19 -0
- package/src/plugins/yaml.js +26 -0
- package/src/plugins.js +54 -0
- package/src/postprocess.js +47 -0
- package/src/render.js +71 -0
- package/src/runtime.js +153 -0
- package/src/tracking.js +74 -0
- package/src/utils.js +65 -0
package/src/render.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import _ from 'lodash'
|
|
4
|
+
|
|
5
|
+
export default async ({ entity, options, config, context, state, logger, port }) => {
|
|
6
|
+
logger = logger || {
|
|
7
|
+
info(...args) {
|
|
8
|
+
port.postMessage(JSON.stringify({ command: 'logger', data: { log: 'info', args } }))
|
|
9
|
+
},
|
|
10
|
+
warn(...args) {
|
|
11
|
+
port.postMessage(JSON.stringify({ command: 'logger', data: { log: 'warn', args } }))
|
|
12
|
+
},
|
|
13
|
+
error(...args) {
|
|
14
|
+
port.postMessage(JSON.stringify({ command: 'logger', data: { log: 'error', args } }))
|
|
15
|
+
},
|
|
16
|
+
trace(...args) {
|
|
17
|
+
port.postMessage(JSON.stringify({ command: 'logger', data: { log: 'trace', args } }))
|
|
18
|
+
},
|
|
19
|
+
notice(...args) {
|
|
20
|
+
port.postMessage(JSON.stringify({ command: 'logger', data: { log: 'notice', args } }))
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function loadPlugin(pluginName) {
|
|
25
|
+
const resolveLocations = [
|
|
26
|
+
path.join(options.workingFolder, 'node_modules', `mikser-core-${pluginName}/index.js`),
|
|
27
|
+
path.join(options.workingFolder, 'plugins', `${pluginName}.js`),
|
|
28
|
+
path.join(path.dirname(import.meta.url), 'plugins', 'render', `${pluginName.replace('render-', '')}.js`)
|
|
29
|
+
]
|
|
30
|
+
for (let resolveLocation of resolveLocations) {
|
|
31
|
+
try {
|
|
32
|
+
return await import(resolveLocation)
|
|
33
|
+
} catch (err) {
|
|
34
|
+
if (err.code != 'ERR_MODULE_NOT_FOUND') {
|
|
35
|
+
logger.error('Redner plugin error:', resolveLocation, err)
|
|
36
|
+
throw err
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const { renderer } = options
|
|
43
|
+
const plugins = {}
|
|
44
|
+
let pluginsToLoad = [...context.plugins || []]
|
|
45
|
+
pluginsToLoad.push(`render-${renderer}`)
|
|
46
|
+
if (entity.meta?.plugins) {
|
|
47
|
+
pluginsToLoad.push(...entity.meta.plugins)
|
|
48
|
+
}
|
|
49
|
+
pluginsToLoad.push(...options.plugins)
|
|
50
|
+
pluginsToLoad = _.uniq(pluginsToLoad.filter(pluginName => pluginName && pluginName.indexOf('render-') == 0))
|
|
51
|
+
|
|
52
|
+
const runtime = {
|
|
53
|
+
[entity.type]: entity,
|
|
54
|
+
entity,
|
|
55
|
+
plugins,
|
|
56
|
+
config: config[`render-${renderer}`],
|
|
57
|
+
data: context.data,
|
|
58
|
+
content() {
|
|
59
|
+
return readFileSync(entity.source, { encoding: 'utf8' })
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for (let pluginName of pluginsToLoad) {
|
|
64
|
+
const plugin = await loadPlugin(pluginName)
|
|
65
|
+
plugins[pluginName] = plugin
|
|
66
|
+
if (plugin?.load) await plugin.load({ entity, options, config: config[pluginName], context, runtime, state, logger })
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const rendererPlugin = plugins[`render-${renderer}`]
|
|
70
|
+
return await rendererPlugin?.render({ entity, options, config, context, plugins, runtime, state, logger })
|
|
71
|
+
}
|
package/src/runtime.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { Mutex } from 'await-semaphore'
|
|
2
|
+
import { AbortError } from './utils.js'
|
|
3
|
+
|
|
4
|
+
const runtime = {
|
|
5
|
+
stamp: Date.now(),
|
|
6
|
+
processTime: undefined,
|
|
7
|
+
engine: {},
|
|
8
|
+
options: {
|
|
9
|
+
plugins: []
|
|
10
|
+
},
|
|
11
|
+
config: {},
|
|
12
|
+
journal: [],
|
|
13
|
+
validators: [],
|
|
14
|
+
started: false,
|
|
15
|
+
mutex: new Mutex(),
|
|
16
|
+
abortController: undefined,
|
|
17
|
+
hooks: {
|
|
18
|
+
initialize: [],
|
|
19
|
+
initialized: [],
|
|
20
|
+
load: [],
|
|
21
|
+
loaded: [],
|
|
22
|
+
import: [],
|
|
23
|
+
validate: [],
|
|
24
|
+
imported: [],
|
|
25
|
+
process: [],
|
|
26
|
+
processed: [],
|
|
27
|
+
persist: [],
|
|
28
|
+
persisted: [],
|
|
29
|
+
beforeRender: [],
|
|
30
|
+
render: [],
|
|
31
|
+
afterRender: [],
|
|
32
|
+
beforePostprocess: [],
|
|
33
|
+
postprocess: [],
|
|
34
|
+
afterPostprocess: [],
|
|
35
|
+
cancel: [],
|
|
36
|
+
cancelled: [],
|
|
37
|
+
finalize: [],
|
|
38
|
+
finalized: [],
|
|
39
|
+
sync: [],
|
|
40
|
+
completed: [],
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
async callHooks(hooks, signal) {
|
|
44
|
+
for (let hook of hooks) {
|
|
45
|
+
if (signal?.aborted) throw new AbortError()
|
|
46
|
+
await hook(signal)
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
addHook(name, hook) {
|
|
51
|
+
if (!this.hooks[name]) throw new Error(`Unknown hook: ${name}`)
|
|
52
|
+
this.hooks[name].push(hook)
|
|
53
|
+
return hook
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
removeHook(name, hook) {
|
|
57
|
+
if (!this.hooks[name]) throw new Error(`Unknown hook: ${name}`)
|
|
58
|
+
const idx = this.hooks[name].indexOf(hook)
|
|
59
|
+
if (idx > -1) this.hooks[name].splice(idx, 1)
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
async start() {
|
|
63
|
+
await this.callHooks(this.hooks.initialize)
|
|
64
|
+
await this.callHooks(this.hooks.initialized)
|
|
65
|
+
await this.callHooks(this.hooks.load)
|
|
66
|
+
await this.callHooks(this.hooks.loaded)
|
|
67
|
+
|
|
68
|
+
await this.callHooks(this.hooks.import)
|
|
69
|
+
await this.callHooks(this.hooks.imported)
|
|
70
|
+
|
|
71
|
+
this.started = true
|
|
72
|
+
await this.process()
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
async process() {
|
|
76
|
+
if (this.abortController?.signal.aborted) return
|
|
77
|
+
else if (this.abortController) {
|
|
78
|
+
await this.cancel()
|
|
79
|
+
}
|
|
80
|
+
this.mutex.use(async () => {
|
|
81
|
+
try {
|
|
82
|
+
this.abortController = new AbortController()
|
|
83
|
+
const { signal } = this.abortController
|
|
84
|
+
|
|
85
|
+
await this.callHooks(this.hooks.process, signal)
|
|
86
|
+
await this.callHooks(this.hooks.processed, signal)
|
|
87
|
+
await this.callHooks(this.hooks.persist, signal)
|
|
88
|
+
await this.callHooks(this.hooks.persisted, signal)
|
|
89
|
+
|
|
90
|
+
await this.render(signal)
|
|
91
|
+
} catch (e) {
|
|
92
|
+
if (e.name !== 'AbortError') throw e
|
|
93
|
+
for (let hook of this.hooks.cancelled) await hook()
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
async render(signal) {
|
|
99
|
+
await this.callHooks(this.hooks.beforeRender, signal)
|
|
100
|
+
await this.callHooks(this.hooks.render, signal)
|
|
101
|
+
await this.callHooks(this.hooks.afterRender, signal)
|
|
102
|
+
|
|
103
|
+
await this.postprocess(signal)
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
async postprocess(signal) {
|
|
107
|
+
await this.callHooks(this.hooks.beforePostprocess, signal)
|
|
108
|
+
await this.callHooks(this.hooks.postprocess, signal)
|
|
109
|
+
await this.callHooks(this.hooks.afterPostprocess, signal)
|
|
110
|
+
|
|
111
|
+
await this.finalize(signal)
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
async cancel() {
|
|
115
|
+
this.abortController?.abort()
|
|
116
|
+
await this.callHooks(this.hooks.cancel)
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
async finalize(signal) {
|
|
120
|
+
await this.callHooks(this.hooks.finalize, signal)
|
|
121
|
+
await this.callHooks(this.hooks.finalized, signal)
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
async sync(operation) {
|
|
125
|
+
let synced
|
|
126
|
+
for (let hook of this.hooks.sync) {
|
|
127
|
+
const result = await hook(operation)
|
|
128
|
+
if (result === true) {
|
|
129
|
+
synced = true
|
|
130
|
+
} else if (result === false && !synced) {
|
|
131
|
+
synced = false
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return synced === undefined || synced
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
async validate(entry) {
|
|
138
|
+
for (let hook of this.validators) {
|
|
139
|
+
if (!await hook(entry)) return false
|
|
140
|
+
}
|
|
141
|
+
return true
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
async complete(entry) {
|
|
145
|
+
let success = true
|
|
146
|
+
for (let hook of this.hooks.completed) {
|
|
147
|
+
if (await hook(entry) === false) success = false
|
|
148
|
+
}
|
|
149
|
+
entry.success = success
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export default runtime
|
package/src/tracking.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import cliProgress from 'cli-progress'
|
|
2
|
+
import runtime from './runtime.js'
|
|
3
|
+
import { useLogger } from './engine.js'
|
|
4
|
+
import formatTime from 'cli-progress/lib/format-time.js'
|
|
5
|
+
import { onInitialized } from './lifecycle.js'
|
|
6
|
+
import util from 'util'
|
|
7
|
+
|
|
8
|
+
let progress = {}
|
|
9
|
+
|
|
10
|
+
function log(log) {
|
|
11
|
+
const { bar, total, value, name } = progress
|
|
12
|
+
const isActive = bar?.isActive
|
|
13
|
+
isActive && bar?.stop()
|
|
14
|
+
log()
|
|
15
|
+
isActive && bar?.start(total, value, { name, details: '' })
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
onInitialized(() => {
|
|
19
|
+
const logger = useLogger()
|
|
20
|
+
if (runtime.options.info) {
|
|
21
|
+
logger.info = (...args) => log(() => console.log(...args))
|
|
22
|
+
logger.warn = (...args) => log(() => console.log('🟡 ' + args[0], ...args.slice(1)))
|
|
23
|
+
logger.error = (...args) => log(() => console.log('🔴 ' + args[0], ...args.slice(1)))
|
|
24
|
+
logger.notice = (...args) => log(() => console.log('🟢 ' + args[0], ...args.slice(1)))
|
|
25
|
+
logger.trace = (...args) => updateProgressDetails(util.format(...args))
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
export function trackProgress(name, total) {
|
|
30
|
+
const logger = useLogger()
|
|
31
|
+
logger.debug('%s started: %d', name, total)
|
|
32
|
+
progress.bar?.stop()
|
|
33
|
+
progress = {
|
|
34
|
+
name,
|
|
35
|
+
total,
|
|
36
|
+
value: 0,
|
|
37
|
+
stamp: Date.now(),
|
|
38
|
+
bar: runtime.options.info ? new cliProgress.SingleBar({
|
|
39
|
+
noTTYOutput: true,
|
|
40
|
+
hideCursor: true,
|
|
41
|
+
clearOnComplete: true,
|
|
42
|
+
barsize: 30,
|
|
43
|
+
format: '{name}: {bar} {percentage}% | ETA: {eta_formatted} {details}',
|
|
44
|
+
}, cliProgress.Presets.shades_grey) : null
|
|
45
|
+
}
|
|
46
|
+
progress.bar?.start(total, 0, { name, details: '' })
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function stopProgress() {
|
|
50
|
+
const logger = useLogger()
|
|
51
|
+
const { value, total, bar, name } = progress
|
|
52
|
+
bar?.stop()
|
|
53
|
+
if (value < total) {
|
|
54
|
+
logger.warn('%s unfinished: %d', name, total - value)
|
|
55
|
+
} else {
|
|
56
|
+
const time = Math.round((Date.now() - progress.stamp) / 1000)
|
|
57
|
+
logger.info('%s: %s', name, formatTime(time, { autopaddingChar: '' }))
|
|
58
|
+
}
|
|
59
|
+
progress = {}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function updateProgress() {
|
|
63
|
+
progress.name && progress.value++
|
|
64
|
+
progress.bar?.increment()
|
|
65
|
+
if (progress?.value == progress?.total) {
|
|
66
|
+
stopProgress()
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function updateProgressDetails(details) {
|
|
71
|
+
const logger = useLogger()
|
|
72
|
+
logger.debug(details)
|
|
73
|
+
progress.bar?.update({ details: `| ${details}` })
|
|
74
|
+
}
|
package/src/utils.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { hashFile, hashingStream } from 'hasha'
|
|
2
|
+
import { stat } from 'node:fs/promises'
|
|
3
|
+
import TruncateStream from 'truncate-stream'
|
|
4
|
+
import { createReadStream } from 'node:fs'
|
|
5
|
+
import _ from 'lodash'
|
|
6
|
+
import { minimatch } from 'minimatch'
|
|
7
|
+
import path from 'path'
|
|
8
|
+
|
|
9
|
+
export class AbortError extends Error {
|
|
10
|
+
constructor(message) {
|
|
11
|
+
super();
|
|
12
|
+
this.name = 'AbortError';
|
|
13
|
+
this.message = message;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function checksum(uri) {
|
|
18
|
+
const maxBytes = 300 * 1024
|
|
19
|
+
const { size } = await stat(uri)
|
|
20
|
+
if (size < maxBytes) {
|
|
21
|
+
return await hashFile(uri, { algorithm: 'md5' })
|
|
22
|
+
} else {
|
|
23
|
+
const truncate = new TruncateStream({ maxBytes })
|
|
24
|
+
const fileStream = createReadStream(uri)
|
|
25
|
+
fileStream.pipe(truncate)
|
|
26
|
+
const checksum = size.toString() + ':' + await hashingStream(truncate, { algorithm: 'md5' })
|
|
27
|
+
return checksum
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function normalize(object) {
|
|
32
|
+
return _.pickBy(
|
|
33
|
+
object,
|
|
34
|
+
(value, key) => {
|
|
35
|
+
let pick = value !== undefined &&
|
|
36
|
+
value !== '' &&
|
|
37
|
+
value !== null &&
|
|
38
|
+
key !== 'undefined' &&
|
|
39
|
+
key !== '' &&
|
|
40
|
+
key !== 'null' &&
|
|
41
|
+
(typeof (value) != 'number' || !isNaN(value))
|
|
42
|
+
return pick
|
|
43
|
+
}
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function matchEntity(entity, match) {
|
|
48
|
+
if (!match) return false
|
|
49
|
+
if (typeof match == 'function') return match(entity)
|
|
50
|
+
else if (typeof match == 'string') {
|
|
51
|
+
if (match.substring(0, 1) == '@/') {
|
|
52
|
+
return minimatch(entity.name, match.substring(2))
|
|
53
|
+
} else {
|
|
54
|
+
return minimatch(entity.id, match)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else if (typeof match == 'object') return _.isMatch(entity, match)
|
|
58
|
+
throw new Error('Ivalid match type')
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function changeExtension(file, format) {
|
|
62
|
+
let extension = path.extname(file)
|
|
63
|
+
let result = file.substring(0, file.length - extension.length) + '.' + format
|
|
64
|
+
return result
|
|
65
|
+
}
|