mikser-io 9.75.0 → 9.76.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mikser-io",
3
- "version": "9.75.0",
3
+ "version": "9.76.0",
4
4
  "files": [
5
5
  "app.js",
6
6
  "index.js",
package/src/engine.js CHANGED
@@ -668,6 +668,15 @@ export async function setup(options) {
668
668
  // A postprocessor consumes the intermediate rendered
669
669
  // file, so skipping would leave its input missing.
670
670
  ? { skip: false, reason: 'postprocessor' }
671
+ // Scheduled BECAUSE the thing that renders it moved. The
672
+ // manifest cannot see that: an asset's input hash is its
673
+ // own source, and a preset's revision is not part of it,
674
+ // so the skip was correct about the entity and wrong about
675
+ // the render. Bumping a preset's `revision` — the
676
+ // documented way to force a rebuild — therefore deleted
677
+ // the stale marker and left the derivative untouched.
678
+ : options.rendererChanged
679
+ ? { skip: false, reason: 'renderer-changed' }
671
680
  : runtime.manifest?.skipDecision(entity, mutatedRefs, currentHashes, mutatedEntities)
672
681
  ?? { skip: false, reason: 'no-manifest' }
673
682
  if (decision.skip) {
@@ -81,6 +81,7 @@ export function assets(options = {}) {
81
81
  onSync,
82
82
  onFinalize,
83
83
  findEntity,
84
+ iterateEntities,
84
85
  matchEntity,
85
86
  changeExtension,
86
87
  constants: { ACTION, OPERATION },
@@ -223,7 +224,7 @@ export function assets(options = {}) {
223
224
  return result
224
225
  }
225
226
 
226
- async function renderPresets(entities) {
227
+ async function renderPresets(entities, { rendererChanged = new Set() } = {}) {
227
228
  const { presets, assetsMap } = runtime.state.assets
228
229
 
229
230
  const tasks = []
@@ -257,6 +258,10 @@ export function assets(options = {}) {
257
258
  ...entity.preset.options, // module defaults
258
259
  ...configOptions, // config overrides
259
260
  renderer: 'preset',
261
+ // The preset itself moved this cycle, so the manifest's
262
+ // "its source is unchanged" is true and beside the
263
+ // point. See the skip decision in engine.js.
264
+ rendererChanged: rendererChanged.has(entityToRender.id),
260
265
  ignore
261
266
  }
262
267
  })
@@ -453,16 +458,41 @@ export function assets(options = {}) {
453
458
  }
454
459
 
455
460
  const entitiesToRender = new Map()
461
+ // Entities scheduled because their PRESET changed, not their source.
462
+ const presetMoved = new Set()
456
463
  await map(useJournal('Assets provision', [OPERATION.CREATE, OPERATION.UPDATE], signal), async ({ entity }) => {
457
464
  if (entity.collection == collection) {
458
- for (let entityId in assetsMap) {
459
- if (assetsMap[entityId].find(preset => preset == entity.name)) {
460
- const entityToRender = await findEntity({
461
- id: entityId
462
- })
463
- if (!entitiesToRender.has(entityToRender.id)) {
464
- entitiesToRender.set(entityToRender.id, entityToRender)
465
- }
465
+ // A preset moved its `revision` was bumped, or its module
466
+ // changed. Everything that uses it has to re-render.
467
+ //
468
+ // Asked of the CATALOG, not of assetsMap. That map is built
469
+ // from this cycle's journal, so on a build where only the
470
+ // preset moved it is empty and this fan-out reached nothing:
471
+ // no asset was scheduled, the reuse check that would have
472
+ // said "the marker is too old" was never consulted, and the
473
+ // startup sweep deleted the old markers anyway because it
474
+ // walks the folder rather than the entities.
475
+ //
476
+ // The result was the worst available shape. Bumping `revision`
477
+ // — the documented way to force a rebuild — removed the marker
478
+ // and left the derivative at its old bytes. It looked like it
479
+ // had worked, nothing in the report said otherwise, and where
480
+ // the derived folder is gitignored the deployment served stale
481
+ // images until someone deleted the folder by hand.
482
+ //
483
+ // getEntityPresets is the same matcher the per-entity path
484
+ // uses, so there is one implementation of "does this entity
485
+ // use this preset" rather than a second one here that can
486
+ // drift.
487
+ for await (const candidate of iterateEntities({ collection: { $ne: collection } })) {
488
+ const candidatePresets = await getEntityPresets(candidate)
489
+ if (!candidatePresets.includes(entity.name)) continue
490
+ // renderPresets reads the presets to render off this map,
491
+ // and on a preset-only build nothing else populated it.
492
+ assetsMap[candidate.id] ??= candidatePresets
493
+ presetMoved.add(candidate.id)
494
+ if (!entitiesToRender.has(candidate.id)) {
495
+ entitiesToRender.set(candidate.id, candidate)
466
496
  }
467
497
  }
468
498
  } else {
@@ -472,7 +502,7 @@ export function assets(options = {}) {
472
502
  }
473
503
  }, { concurrency: 10, signal })
474
504
 
475
- await renderPresets(entitiesToRender.values())
505
+ await renderPresets(entitiesToRender.values(), { rendererChanged: presetMoved })
476
506
  })
477
507
 
478
508
  onComplete(async ({ entity, options }) => {