mikser-io 8.3.9 → 9.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/CLAUDE.md CHANGED
@@ -81,7 +81,10 @@ brevity.
81
81
  (`minThreads: 0` + `idleTimeout: 30_000`) so INLINE-only workloads
82
82
  pay no worker overhead. `workerSafeOptions(runtime.options)`
83
83
  strips plugin-surface functions before TASKS.WORKER dispatch so
84
- Piscina's structured clone doesn't choke.
84
+ Piscina's structured clone doesn't choke. Renderer / postprocessor
85
+ descriptors in `runtime.options.plugins` are projected to their
86
+ `render-${name}` / `post-${name}` identifier so workers can
87
+ resolve them via dynamic import.
85
88
  - `database/` — `createSqliteDatabase()`, `registerSchema()`,
86
89
  `useDatabase()` (the `mikser_meta` table stamps schema_version).
87
90
  `sift-to-sql.js` translates sift filters to SQL WHERE clauses
@@ -111,10 +114,27 @@ brevity.
111
114
  the engine's logger. Each worker opens its own read-only sqlite
112
115
  handle on first task (`ensureWorkerDb` in render.js) so template
113
116
  helpers like `runtime.lookupHref` stay sync. Never touch the journal
114
- directly.
115
- - `config.js` loads `mikser.config.js` at `onLoad`.
116
- - `plugins.js` loads user plugins at `onLoad`. Plugin factories
117
- receive the full `core` exports as their first argument.
117
+ directly. Plugin loading: main-thread INLINE dispatch reads
118
+ `runtime.renderers` / `runtime.postprocessors` first (populated by
119
+ plugins.js from descriptor returns); workers see empty registries
120
+ in their separate-process runtime and fall through to dynamic-import
121
+ by `mikser-io-${name}` package name. Per-plugin options flow through
122
+ `descriptor.options` and arrive as the `config` arg to
123
+ `load`/`render`/`setup`/`postprocess`/`teardown`.
124
+ - `config.js` — loads `mikser.config.js` at `onLoad` into
125
+ `runtime.config`. v9 holds only engine-level keys (`server`,
126
+ `logging`, `catalog` if tuned) plus the `plugins` array — all
127
+ plugin options moved to the factory call site (ADR-0010).
128
+ - `plugins.js` — dispatches v9 plugin entries at `onLoad`. Each
129
+ entry in `plugins: []` is a factory call return; the dispatcher
130
+ duck-types on shape:
131
+ - function → lifecycle plugin; called with `core` so it can
132
+ register hooks.
133
+ - `{ name, options, load?, render? }` → renderer descriptor;
134
+ stored in `runtime.renderers`.
135
+ - `{ name, options, postprocess, output?, setup?, teardown? }` →
136
+ postprocessor descriptor; stored in `runtime.postprocessors`.
137
+ Strings produce a v9 migration error pointing at the new shape.
118
138
  - `manager.js` — file watching (chokidar) and cron scheduling.
119
139
  - `source.js` — `useSource` codifies the folder-of-files pattern.
120
140
  - `constants.js` — `OPERATION` (CREATE/UPDATE/DELETE/RENDER/
@@ -149,7 +169,10 @@ brevity.
149
169
  `import { queryEntities, subscribe, useRenderer, useCollection,
150
170
  readEntityContent, isTextEntity } from 'mikser-io'`.
151
171
  - **Plugin packages**: `mikser-io-<name>` (mikser-io-mcp, mikser-io-vector,
152
- mikser-io-schemas, etc.).
172
+ mikser-io-schemas, etc.). Each exports a v9 named factory in
173
+ camelCase: `import { vector } from 'mikser-io-vector'`,
174
+ `import { renderHbs } from 'mikser-io'`. Consumer uses
175
+ `plugins: [vector({...})]` — never the bare string.
153
176
  - **MCP tools**: `mikser_<verb>` or `mikser_<subsystem>_<verb>`:
154
177
  `mikser_query_entities`, `mikser_read_entity`, `mikser_update_entity`,
155
178
  `mikser_delete_entity`, `mikser_render`, `mikser_refs_inbound`,
@@ -206,25 +229,39 @@ brevity.
206
229
  persistence pattern. Journal-on-sqlite (Phase 7) enables `--resume`;
207
230
  auto-persist (Phase 9) means plugins mutate the yielded entity and
208
231
  the journal writes back without an explicit `updateEntry` call.
232
+ - **0010** — Plugin bundles + factory-call form + inline options.
233
+ Plugins are imported by name and called as factories;
234
+ `plugins: []` carries factory returns, never strings.
235
+ Lifecycle plugins are `(options) => (core) => void`; renderers
236
+ return `{name, options, load?, render?}`; postprocessors return
237
+ `{name, options, output?, setup?, postprocess, teardown?}`. Per-
238
+ plugin config moved entirely off `runtime.config.<plugin>`; it
239
+ arrives as factory args, gets stashed on the descriptor, and is
240
+ passed as `config` to `load`/`render`/`setup`/`postprocess`.
209
241
 
210
242
  ## MCP
211
243
 
212
- Lives in `mikser-io-mcp` plugin (separate repo). Activate by listing
213
- `'mcp'` **first** in your `mikser.config.js` plugins array:
244
+ Lives in `mikser-io-mcp` plugin (separate repo). Activate by calling
245
+ `mcp()` **first** in your `mikser.config.js` plugins array:
214
246
 
215
247
  ```js
248
+ import { mcp } from 'mikser-io-mcp'
249
+
216
250
  export default {
217
- plugins: ['mcp', /* ...other plugins */],
218
- mcp: {
219
- path: '/mcp', // optional; default '/mcp'
220
- // endpoints: { ... } // optional; per-endpoint token + scope
221
- },
251
+ plugins: [
252
+ mcp({
253
+ // path: '/mcp', // default '/mcp'
254
+ // endpoints: { ... }, // per-endpoint token + scope
255
+ }),
256
+ /* ...other plugins */
257
+ ],
222
258
  }
223
259
  ```
224
260
 
225
261
  Must be first because its factory creates `runtime.options.mcp`
226
- synchronously, and other plugins gate their MCP tool registration on
227
- `if (runtime.options.mcp)` in their own `onLoaded`.
262
+ synchronously when its closure runs, and other plugins gate their
263
+ MCP tool registration on `if (runtime.options.mcp)` in their own
264
+ `onLoaded`.
228
265
 
229
266
  There is **no `--mcp` CLI flag**. Activation is plugin-presence only.
230
267
 
@@ -276,9 +313,13 @@ There is **no `--mcp` CLI flag**. Activation is plugin-presence only.
276
313
 
277
314
  - **New engine capability?** Run through ADR-0006's five tests. Bar
278
315
  is high. Express is the only earned addition.
279
- - **New plugin?** Own repo, named `mikser-io-<name>`. Composes
280
- against `runtime.options.app` / `runtime.options.mcp` / lifecycle
281
- hooks. Never imports another plugin's source.
316
+ - **New plugin?** Own repo, named `mikser-io-<name>`. Exports a
317
+ named v9 factory (e.g. `export function vector(options = {}) {
318
+ return (core) => { ... } }`). Composes against
319
+ `runtime.options.app` / `runtime.options.mcp` / lifecycle hooks.
320
+ Never imports another plugin's source — and the engine never
321
+ reads `runtime.config.<plugin>` for plugin options; everything
322
+ flows through the factory arg (ADR-0010).
282
323
  - **New MCP tool?** Add to `mikser-io-mcp/index.js` via
283
324
  `mcp.simpleTool(name, description, zodSchema, handler)`. Tool name
284
325
  follows `mikser_*` convention.
package/README.md CHANGED
@@ -92,9 +92,11 @@ Plugins extend the tool surface the same way they mount HTTP routes; install the
92
92
 
93
93
  ```js
94
94
  // mikser.config.js
95
+ import { mcp } from 'mikser-io-mcp'
96
+
95
97
  export default {
96
- plugins: ['mcp', /* … */],
97
- // optional: mcp: { path: '/mcp', endpoints: { … } }
98
+ plugins: [mcp(), /* … */],
99
+ // mcp({ path: '/mcp', endpoints: { … } }) when options are needed
98
100
  }
99
101
  ```
100
102
 
package/index.js CHANGED
@@ -15,4 +15,35 @@ export * from './src/manager.js'
15
15
  export * from './src/logger.js'
16
16
  export * from './src/engine.js'
17
17
  export * from './src/render.js'
18
- export * from './src/source.js'
18
+ export * from './src/source.js'
19
+
20
+ // Built-in plugin factories. Each takes options and returns the
21
+ // (core) => void closure the engine calls at onLoad time. See ADR-0010
22
+ // for the v9 plugin shape.
23
+ export { api } from './src/plugins/api.js'
24
+ export { assets } from './src/plugins/assets.js'
25
+ export { commands } from './src/plugins/commands.js'
26
+ export { data } from './src/plugins/data.js'
27
+ export { documents } from './src/plugins/documents.js'
28
+ export { files } from './src/plugins/files.js'
29
+ export { frontMatter } from './src/plugins/front-matter.js'
30
+ export { json } from './src/plugins/json.js'
31
+ export { layouts } from './src/plugins/layouts.js'
32
+ export { mapper } from './src/plugins/mapper.js'
33
+ export { observer } from './src/plugins/observer.js'
34
+ export { preview } from './src/plugins/preview.js'
35
+ export { resources } from './src/plugins/resources.js'
36
+ export { shares } from './src/plugins/shares.js'
37
+ export { validator } from './src/plugins/validator.js'
38
+ export { yaml } from './src/plugins/yaml.js'
39
+
40
+ // Built-in renderers. v9 factory shape returns the descriptor that the
41
+ // loader stores in `runtime.renderers`; the same module also still
42
+ // exports `load`/`render` at the top level so Piscina worker dispatch
43
+ // can resolve via dynamic import. ADR-0010.
44
+ export { renderAsset } from './src/plugins/render/asset.js'
45
+ export { renderFile } from './src/plugins/render/file.js'
46
+ export { renderHbs } from './src/plugins/render/hbs.js'
47
+ export { renderHref } from './src/plugins/render/href.js'
48
+ export { renderPreset } from './src/plugins/render/preset.js'
49
+ export { renderResource } from './src/plugins/render/resource.js'
package/package.json CHANGED
@@ -1,8 +1,13 @@
1
1
  {
2
2
  "name": "mikser-io",
3
- "version": "8.3.9",
3
+ "version": "9.0.0",
4
4
  "description": "<p align=\"center\"> <img src=\"mikser-lockup-stacked.svg\" alt=\"mikser\" width=\"198\" /> </p>",
5
5
  "main": "index.js",
6
+ "exports": {
7
+ ".": "./index.js",
8
+ "./package.json": "./package.json",
9
+ "./*": "./*"
10
+ },
6
11
  "scripts": {
7
12
  "debug": "node --no-warnings app.js --server --watch --working-folder test/fixture",
8
13
  "test:unit": "node --test --test-reporter=spec 'test/unit/**/*.test.js'",
package/src/config.js CHANGED
@@ -4,34 +4,25 @@ import { onLoad } from './lifecycle.js'
4
4
  import path from 'node:path'
5
5
 
6
6
  onLoad(async () => {
7
- const logger = useLogger()
8
- const configFile = path.resolve(runtime.options.config)
9
- logger.info('Config: %s', configFile)
10
- try {
11
- const config = await import(configFile)
12
- if (typeof config.default == 'function') {
13
- runtime.config = await config.default(runtime)
14
- } else if (typeof config.default == 'object') {
15
- runtime.config = config.default
16
- }
17
- } catch (err) {
18
- if (err.code != 'ERR_MODULE_NOT_FOUND') throw err
19
- }
7
+ const logger = useLogger()
8
+ const configFile = path.resolve(runtime.options.config)
9
+ logger.info('Config: %s', configFile)
10
+ try {
11
+ const config = await import(configFile)
12
+ if (typeof config.default == 'function') {
13
+ runtime.config = await config.default(runtime)
14
+ } else if (typeof config.default == 'object') {
15
+ runtime.config = config.default
16
+ }
17
+ } catch (err) {
18
+ if (err.code != 'ERR_MODULE_NOT_FOUND') throw err
19
+ }
20
20
 
21
- const plugins = runtime.options.plugins.concat(runtime.config.plugins).filter((plugin) => plugin)
22
- for (const plugin of plugins) {
23
- if (!runtime.config[plugin]) {
24
- try {
25
- const pluginConfig = path.join(runtime.options.workingFolder, 'config', `${plugin}.config.js`)
26
- const config = await import(pluginConfig)
27
- if (typeof config.default == 'function') {
28
- runtime.config[plugin] = await config.default(runtime)
29
- } else if (typeof config.default == 'object') {
30
- runtime.config[plugin] = config.default
31
- }
32
- } catch (err) {
33
- if (err.code != 'ERR_MODULE_NOT_FOUND') throw err
34
- }
35
- }
36
- }
21
+ // v8 used to walk `runtime.config.plugins` looking for matching
22
+ // `config/<plugin>.config.js` files to merge into `runtime.config`,
23
+ // because plugin entries were strings (names). v9 entries are factory
24
+ // call results (closures or descriptors), and plugin options arrive
25
+ // as factory args see ADR-0010 — so per-plugin auxiliary config
26
+ // files have nothing to bind to. The loader was removed when the
27
+ // plugins list stopped carrying names.
37
28
  })
package/src/engine.js CHANGED
@@ -38,6 +38,24 @@ import { queryContext } from './database/query-context.js'
38
38
  function workerSafeOptions(opts) {
39
39
  const result = {}
40
40
  for (const [k, v] of Object.entries(opts)) {
41
+ // `plugins` is a mixed array of factory-return values — functions
42
+ // (lifecycle plugins; workers don't need them) and descriptor
43
+ // objects (renderers / postprocessors) carrying closures that
44
+ // can't survive structuredClone. Project descriptors to their
45
+ // `render-${name}` / `post-${name}` identifiers so the worker
46
+ // can resolve them via dynamic import.
47
+ if (k === 'plugins' && Array.isArray(v)) {
48
+ result[k] = v
49
+ .map(p => {
50
+ if (p && typeof p === 'object' && typeof p.name === 'string'
51
+ && (typeof p.load === 'function' || typeof p.render === 'function')) return `render-${p.name}`
52
+ if (p && typeof p === 'object' && typeof p.name === 'string'
53
+ && typeof p.postprocess === 'function') return `post-${p.name}`
54
+ return null
55
+ })
56
+ .filter(Boolean)
57
+ continue
58
+ }
41
59
  try {
42
60
  structuredClone(v)
43
61
  result[k] = v
@@ -89,7 +107,6 @@ export async function setup(options) {
89
107
  onInitialize(async () => {
90
108
  runtime.engine.commander?.version(packageInfo.version)
91
109
  .option('-i --working-folder <folder>', 'set mikser working folder', './')
92
- .option('-p --plugins [plugins...]', 'list of mikser plugins to load', [])
93
110
  .option('-c --config <file>', 'set mikser mikser.config.js location', './mikser.config.js')
94
111
  .option('-m --mode <mode>', 'set mikser runtime mode', 'development')
95
112
  .option('-r --clear', 'clear current state before execution', false)
@@ -203,16 +220,6 @@ export async function setup(options) {
203
220
  onRender(async (signal) => {
204
221
  const logger = useLogger()
205
222
  const renderJobs = new Set()
206
- // Computed once per cycle — runtime.config doesn't mutate
207
- // mid-cycle, so every render gets the same filtered slice.
208
- // Native key iteration + String#startsWith; was a per-render
209
- // `_.pickBy(runtime.config, (v,k) => _.startsWith(k,'render-'))`
210
- // which charged ~20 predicate calls + 1 object allocation per
211
- // entry. Symmetric with onPostprocess's `config` below.
212
- const renderConfig = {}
213
- for (const key in runtime.config) {
214
- if (key.startsWith('render-')) renderConfig[key] = runtime.config[key]
215
- }
216
223
 
217
224
  // Collect this cycle's mutated entity ids/hrefs/entities so the
218
225
  // manifest skip check can re-render anything whose dependencies
@@ -326,7 +333,11 @@ export async function setup(options) {
326
333
  ...runtime.options,
327
334
  ...options,
328
335
  },
329
- config: renderConfig,
336
+ // Per-renderer options live on each renderer
337
+ // descriptor (.options) and are picked up inside
338
+ // render.js at dispatch time — no top-level config
339
+ // channel anymore (ADR-0010).
340
+ config: {},
330
341
  context,
331
342
  state: runtime.state,
332
343
  track,
@@ -470,25 +481,13 @@ export async function setup(options) {
470
481
  for await (const { entity, options, context, output } of useJournal('Queuing postprocess', [OPERATION.RENDER], signal)) {
471
482
  if (output?.success && options.postprocessor) {
472
483
  const ext = await resolveOutputExt(options.postprocessor)
473
- let destination = changeExtension(entity.destination, ext)
474
-
475
- // cleanUrls renders a non-index page `foo` to
476
- // `foo/index.html` so the served URL is `/foo/`. A
477
- // postprocessor that emits a *different* file type (e.g.
478
- // PDF) shouldn't inherit that clean-URL folder — a
479
- // downloadable artifact wants to be `foo.pdf`, not
480
- // `foo/index.pdf`. Collapse the `/index` segment when the
481
- // produced extension differs from the rendered page's.
482
- // Genuine index documents (name ends in `index`) keep
483
- // their path.
484
- const originExt = path.extname(entity.destination).slice(1)
485
- const isCleanUrlPage =
486
- runtime.config.layouts?.cleanUrls &&
487
- !_.endsWith(entity.name, 'index') &&
488
- path.basename(entity.destination, path.extname(entity.destination)) === 'index'
489
- if (ext !== originExt && isCleanUrlPage) {
490
- destination = `${path.dirname(entity.destination)}.${ext}`
491
- }
484
+ // Engine swaps the extension on whatever destination the
485
+ // entity carries. Routing semantics (cleanUrls folder
486
+ // structure, alternate placements, etc.) are whoever-set-
487
+ // entity.destination's responsibility typically the
488
+ // layouts plugin during layout-match. The postprocess
489
+ // subsystem has no opinion about routing.
490
+ const destination = changeExtension(entity.destination, ext)
492
491
 
493
492
  tasks.push({
494
493
  entity: {
@@ -521,14 +520,23 @@ export async function setup(options) {
521
520
 
522
521
  onPostprocess(async (signal) => {
523
522
  const logger = useLogger()
524
- const config = _.pickBy(runtime.config, (value, key) => _.startsWith(key, 'post-'))
525
523
 
524
+ // Collect every postprocessor descriptor in the plugins list
525
+ // and project to the `post-${name}` identifier the loader uses.
526
+ // Per-postprocessor options now live on each descriptor (.options)
527
+ // — no top-level `config['post-*']` channel anymore (ADR-0010).
528
+ const postPluginNames = []
529
+ for (const entry of runtime.options.plugins) {
530
+ if (entry && typeof entry === 'object' && typeof entry.postprocess === 'function' && typeof entry.name === 'string') {
531
+ postPluginNames.push(`post-${entry.name}`)
532
+ }
533
+ }
526
534
  const postPlugins = {}
527
- for (const pluginName of runtime.options.plugins.filter(p => p.startsWith('post-'))) {
535
+ for (const pluginName of postPluginNames) {
528
536
  const plugin = await loadPostPlugin(pluginName, runtime.options.workingFolder)
529
537
  if (plugin) {
530
538
  postPlugins[pluginName] = plugin
531
- if (plugin.setup) await plugin.setup({ options: runtime.options, config: config[pluginName], state: runtime.state, logger })
539
+ if (plugin.setup) await plugin.setup({ options: runtime.options, config: plugin.options, state: runtime.state, logger })
532
540
  }
533
541
  }
534
542
 
@@ -546,7 +554,11 @@ export async function setup(options) {
546
554
  ...runtime.options,
547
555
  ...options,
548
556
  },
549
- config,
557
+ // Per-postprocessor options live on the descriptor
558
+ // and are read inside postprocess.js at dispatch
559
+ // time; the empty object here keeps the worker
560
+ // arg shape stable.
561
+ config: {},
550
562
  context,
551
563
  state: runtime.state
552
564
  }
@@ -632,7 +644,7 @@ export async function setup(options) {
632
644
  postprocessJobs.size && logger.info('Postprocessed: %d', postprocessJobs.size)
633
645
  } finally {
634
646
  for (const [pluginName, plugin] of Object.entries(postPlugins)) {
635
- if (plugin.teardown) await plugin.teardown({ options: runtime.options, config: config[pluginName], state: runtime.state, logger })
647
+ if (plugin.teardown) await plugin.teardown({ options: runtime.options, config: plugin.options, state: runtime.state, logger })
636
648
  }
637
649
  }
638
650
  })
@@ -312,18 +312,19 @@ function sseSend(res, eventName, payload) {
312
312
  } catch { /* connection dropped — cleanup runs via 'close' */ }
313
313
  }
314
314
 
315
- export default ({
316
- runtime,
317
- onLoaded,
318
- onFinalize,
319
- useLogger,
320
- useJournal,
321
- constants: { OPERATION },
322
- }) => {
315
+ export function api(options = {}) {
316
+ return ({
317
+ runtime,
318
+ onLoaded,
319
+ onFinalize,
320
+ useLogger,
321
+ useJournal,
322
+ constants: { OPERATION },
323
+ }) => {
323
324
  // Shared between onLoaded (populates) and onFinalize (consumes).
324
325
  // Hoisted so the lifecycle hooks see the same registry; the body
325
326
  // inside onLoaded is what actually fills it in based on
326
- // runtime.config.api.endpoints.
327
+ // `options.endpoints`.
327
328
  let apiBase = '/api'
328
329
  const cachedEndpoints = []
329
330
 
@@ -343,9 +344,9 @@ export default ({
343
344
  )
344
345
  }
345
346
 
346
- const endpoints = runtime.config.api?.endpoints
347
+ const endpoints = options.endpoints
347
348
  if (!endpoints || !Object.keys(endpoints).length) {
348
- logger.warn('Api plugin loaded but no endpoints configured (api.endpoints) — nothing to mount')
349
+ logger.warn('Api plugin loaded but no endpoints configured (pass { endpoints: {...} } to api()) — nothing to mount')
349
350
  return
350
351
  }
351
352
 
@@ -353,10 +354,10 @@ export default ({
353
354
  throw new Error('Express is required for the api plugin — run: npm install express')
354
355
  })
355
356
 
356
- apiBase = runtime.config.api?.base ?? '/api'
357
+ apiBase = options.base ?? '/api'
357
358
  const base = apiBase // alias so existing local references still work
358
- const globalPageSize = runtime.config.api?.pageSize ?? 10
359
- const globalRenderTimeout = runtime.config.api?.renderTimeout ?? 30_000
359
+ const globalPageSize = options.pageSize ?? 10
360
+ const globalRenderTimeout = options.renderTimeout ?? 30_000
360
361
 
361
362
  // Preview workflow (render → cache → URL) lives in its own
362
363
  // plugin (src/plugins/preview.js) as of v7.3.0. The api plugin
@@ -916,4 +917,5 @@ export default ({
916
917
  }
917
918
  }
918
919
  })
920
+ }
919
921
  }
@@ -6,7 +6,7 @@ import { globby } from 'globby'
6
6
  import _ from 'lodash'
7
7
  import map from 'p-map'
8
8
 
9
- // Normalize a `runtime.config.assets.presets[name]` value to a consistent
9
+ // Normalize a `options.presets[name]` value to a consistent
10
10
  // { matches, options } shape so callers don't have to inspect which form
11
11
  // the config used.
12
12
  //
@@ -48,35 +48,36 @@ export function normalizePresetConfig(value) {
48
48
  return { matches: [value], options: {} }
49
49
  }
50
50
 
51
- export default ({
52
- runtime,
53
- onLoaded,
54
- useLogger,
55
- onImport,
56
- watch,
57
- onProcessed,
58
- onBeforeRender,
59
- useJournal,
60
- createEntity,
61
- updateEntity,
62
- deleteEntity,
63
- renderEntities,
64
- onComplete,
65
- onSync,
66
- onFinalize,
67
- findEntity,
68
- matchEntity,
69
- changeExtension,
70
- constants: { ACTION, OPERATION },
71
- }) => {
51
+ export function assets(options = {}) {
52
+ return ({
53
+ runtime,
54
+ onLoaded,
55
+ useLogger,
56
+ onImport,
57
+ watch,
58
+ onProcessed,
59
+ onBeforeRender,
60
+ useJournal,
61
+ createEntity,
62
+ updateEntity,
63
+ deleteEntity,
64
+ renderEntities,
65
+ onComplete,
66
+ onSync,
67
+ onFinalize,
68
+ findEntity,
69
+ matchEntity,
70
+ changeExtension,
71
+ constants: { ACTION, OPERATION },
72
+ }) => {
72
73
  const collection = 'presets'
73
74
  const type = 'preset'
74
75
  const checksumMap = new Set()
75
76
 
76
77
  async function getEntityPresets(entity) {
77
78
  const entityPresets = []
78
- for (let preset in (runtime.config.assets?.presets || {})) {
79
- const { matches } = normalizePresetConfig(runtime.config.assets.presets[preset])
79
+ for (let preset in (options.presets || {})) {
80
+ const { matches } = normalizePresetConfig(options.presets[preset])
80
81
  for (let match of matches) {
81
82
  if (matchEntity(entity, match)) {
82
83
  entityPresets.push(preset)
@@ -178,7 +179,7 @@ export default ({
178
179
  // picked up on the next cycle without rebuilding the
179
180
  // preset entity from its module.
180
181
  const { options: configOptions } = normalizePresetConfig(
181
- runtime.config.assets?.presets?.[entityPreset]
182
+ options.presets?.[entityPreset]
182
183
  )
183
184
  let destination = entity.name
184
185
  if (entity.preset.format) {
@@ -204,27 +205,27 @@ export default ({
204
205
  onLoaded(async () => {
205
206
  const logger = useLogger()
206
207
 
207
- const assetsName = runtime.config.assets?.assetsFolder || 'assets'
208
+ const assetsName = options.assetsFolder || 'assets'
208
209
  runtime.state.assets = {
209
210
  presets: {},
210
211
  assetsMap: {},
211
- assetsFolder: runtime.config.assets?.outputFolder
212
- ? path.join(runtime.config.assets.outputFolder, assetsName)
212
+ assetsFolder: options.outputFolder
213
+ ? path.join(options.outputFolder, assetsName)
213
214
  : assetsName,
214
215
  }
215
216
 
216
- runtime.options.presets = runtime.config.presets?.presetsFolder || collection
217
+ runtime.options.presets = options.presetsFolder || collection
217
218
  runtime.options.presetsFolder = path.join(runtime.options.workingFolder, runtime.options.presets)
218
219
  logger.debug('Presets folder: %s', runtime.options.presetsFolder)
219
220
  await mkdir(runtime.options.presetsFolder, { recursive: true })
220
221
 
221
- runtime.options.assets = runtime.config.assets?.assetsFolder || 'assets'
222
+ runtime.options.assets = options.assetsFolder || 'assets'
222
223
  runtime.options.assetsFolder = path.join(runtime.options.workingFolder, runtime.options.assets)
223
224
  logger.debug('Assets folder: %s', runtime.options.assetsFolder)
224
225
  await mkdir(runtime.options.assetsFolder, { recursive: true })
225
226
 
226
227
  let link = path.join(runtime.options.outputFolder, runtime.options.assets)
227
- if (runtime.config.assets?.outputFolder) link = path.join(runtime.options.outputFolder, runtime.config.assets?.outputFolder, runtime.options.assets)
228
+ if (options.outputFolder) link = path.join(runtime.options.outputFolder, options.outputFolder, runtime.options.assets)
228
229
  try {
229
230
  await mkdir(path.dirname(link), { recursive: true })
230
231
  await symlink(path.resolve(runtime.options.assetsFolder), link, 'dir')
@@ -317,7 +318,7 @@ export default ({
317
318
  // `mikser-io-preset-<name>`. Config is the source of truth for
318
319
  // which presets a project uses; this fills the names a folder
319
320
  // scan can't (the code lives in node_modules, not presets/).
320
- for (const name of Object.keys(runtime.config.assets?.presets || {})) {
321
+ for (const name of Object.keys(options.presets || {})) {
321
322
  if (presets[name]) continue // a local file already loaded it
322
323
  const resolved = resolvePreset(name)
323
324
  if (!resolved) {
@@ -430,8 +431,9 @@ export default ({
430
431
  }
431
432
  })
432
433
 
433
- return {
434
- collection,
435
- type
434
+ return {
435
+ collection,
436
+ type,
437
+ }
436
438
  }
437
439
  }