@ranx729/ducktape-assets 1.0.0 → 1.1.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/src/resolver.mjs CHANGED
@@ -100,19 +100,28 @@ export function createResolver({ index, base, formats = ['avif', 'webp'], pin =
100
100
  const { setId, entry, asset } = ctx;
101
101
  const maxDpr = maxOf(d ?? dpr, 3);
102
102
  const sources = [];
103
+ const files = [];
104
+ const fileOf = (fmt, r) => ({ format: fmt, url: urlOf(entry, r, setId), path: r.path, width: r.w, height: r.h, bytes: r.bytes, sha256: r.sha256, ...(r.package ? { package: r.package } : {}) });
103
105
  let fallback;
106
+ let fallbackFmt;
104
107
  for (const fmt of formats) {
105
108
  const ladder = asset.sources?.[fmt];
106
109
  if (!Array.isArray(ladder) || !ladder.length) continue;
107
110
  const sel = selectRungs(ladder, width, { maxDpr });
108
111
  sources.push({ type: mime(fmt), srcset: sel.rungs.map((r) => `${urlOf(entry, r, setId)} ${r.w}w`).join(', ') });
112
+ files.push(...sel.rungs.map((r) => fileOf(fmt, r)));
109
113
  fallback = sel;
114
+ fallbackFmt = fmt;
110
115
  }
111
116
  if (!fallback) throw new Error(`${id}: none of the formats ${formats.join(', ')} is available`);
112
117
  const out = {
113
118
  src: urlOf(entry, fallback.src, setId),
114
119
  srcset: sources[sources.length - 1].srcset,
115
120
  sources,
121
+ // The chosen files with their bytes and sha256, for build scripts that record or verify what they reference.
122
+ // `file` is the one behind `src`; `files` lists every rung of every format in the srcsets.
123
+ file: fileOf(fallbackFmt, fallback.src),
124
+ files,
116
125
  sizes: sizes ?? (width ? `${width}px` : '100vw'),
117
126
  width: asset.width,
118
127
  height: asset.height,
@@ -197,13 +206,17 @@ export function createResolver({ index, base, formats = ['avif', 'webp'], pin =
197
206
  const { setId, entry, asset } = await get(id);
198
207
  const f = asset.render?.frame;
199
208
  if (!f) throw new Error(`${id} has no render.frame`);
209
+ // Composed atlases record one percentage; ready-made frames record four (top right bottom left) because a uniform
210
+ // pixel slice is a different share of the width and of the height. The CSS value and the coverage maths follow.
200
211
  const slice = f.slice;
212
+ const sliceCss = typeof slice === 'number' ? `${slice}%` : `${slice.top}% ${slice.right}% ${slice.bottom}% ${slice.left}%`;
213
+ const sliceW = typeof slice === 'number' ? slice : slice.left; // share of the atlas width that must cover `thickness`
201
214
  const atlas = f.atlas;
202
215
  const dprs = dprList(d ?? dpr, [1, 2, 3]);
203
216
  const smallest = Object.values(atlas).find(Array.isArray)?.[0];
204
- thickness ??= smallest ? Math.round((smallest.w * slice) / 100) : 16;
205
- // the slice region (slice% of the atlas) must map onto `thickness` css px at density x
206
- const need = (x) => (thickness * x * 100) / slice;
217
+ thickness ??= smallest ? Math.round((smallest.w * sliceW) / 100) : 16;
218
+ // the slice region (sliceW% of the atlas width) must map onto `thickness` css px at density x
219
+ const need = (x) => (thickness * x * 100) / sliceW;
207
220
  let value;
208
221
  if (atlas.svg) value = cssUrl(urlOf(entry, atlas.svg, setId));
209
222
  else value = imageSet(entry, setId, atlas, 'w', thickness, dprs, need).set;
@@ -214,8 +227,8 @@ export function createResolver({ index, base, formats = ['avif', 'webp'], pin =
214
227
  .map((r) => `${urlOf(entry, r, setId)} ${r.w}w`)
215
228
  .join(', ')
216
229
  : '';
217
- const css = [`border-style: solid;`, `border-width: ${thickness}px;`, `border-image: ${value} ${slice}% round;`].join('\n');
218
- return { css, slice, atlasSrcset };
230
+ const css = [`border-style: solid;`, `border-width: ${thickness}px;`, `border-image: ${value} ${sliceCss} round;`].join('\n');
231
+ return { css, slice, sliceCss, atlasSrcset };
219
232
  }
220
233
 
221
234
  /** Paths of the files the copy command writes for an asset (all rungs), for flows that import local files. */