@open-assets/open-assets 0.3.0 → 0.5.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 CHANGED
@@ -1,20 +1,40 @@
1
1
  <p align="center">
2
- <img src="public/img/banner/1280/banner.png" alt="Open Assets" width="100%" />
2
+ <img src="public/img/banner/1280.png" alt="Open Assets" width="100%" />
3
3
  </p>
4
4
 
5
5
  # Open Assets
6
6
 
7
- The LLM first way to do graphics all from your git repo.
7
+ [![npm version](https://img.shields.io/npm/v/@open-assets/open-assets)](https://www.npmjs.com/package/@open-assets/open-assets)
8
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
9
+ [![Claude Code Skill](https://img.shields.io/badge/Claude%20Code-skill%20included-d97757)](#2-design-with-claude-code)
8
10
 
9
- Dev server and export tool for app marketing assets. Like Storybook, but for marketing assets.
11
+ **Screenshots, icons, and marketing assets, made a breeze in your git repo.**
10
12
 
11
- Design your App Store screenshots, app icons, logos, OG images, favicons, and more in HTML/CSS/SVG, preview them live in the browser, and export pixel-perfect PNGs at any size — including directly into your Xcode project.
13
+ Design App Store screenshots, app icons, logos, OG images, and favicons as HTML/CSS/SVG templates. Preview them live in the browser. Render every size your stores need from a single template with one command, then export the results anywhere: your exports folder, your web app's public directory, or straight into an Xcode asset catalog.
14
+
15
+ Like Storybook, but for marketing assets. Free, local, MIT licensed, and built for AI coding agents.
16
+
17
+ ## Why Open Assets
18
+
19
+ - **One template, every size.** Author a screenshot once at its source size and render it for iPhone 6.9", 6.7", iPad, Play Store, Product Hunt, and any custom dimension. No per-device rework.
20
+ - **Export to multiple locations.** Each export size can write to its own destination with `outFile`: drop `favicon.png` into `public/`, send the OG image to your web app, and write the app icon directly into your Xcode `.appiconset`.
21
+ - **Localization built in.** One template renders in every language. Mustache-style `{{placeholders}}`, locale-aware number formatting, and automatic RTL layout for Arabic, Hebrew, and friends. See [Localization](#localization).
22
+ - **Live preview dev server.** A Storybook-style UI with hot reload, zoom/pan, and one-click exports while you iterate on templates.
23
+ - **Incremental renders.** `assets.lock` checksums every source file, so you, your teammates, and CI only re-render what changed.
24
+ - **Versioned in git, rendered in CI.** No SaaS, no per-seat design tool. Review screenshot changes in a PR with `git diff`, re-render on every push with GitHub Actions.
25
+ - **Agent native.** Ships a Claude Code skill (`npx open-assets skills`), so your agent can design, localize, and render the entire asset set for you.
12
26
 
13
27
  ## Examples
14
28
 
15
- <p><img src="public/img/examples/only-recipes-icon.png" width="14" height="14" /> <a href="https://onlyrecipes.app"><strong>Only Recipes</strong></a> (<a href="examples/only-recipes">examples</a>)</p>
29
+ <p><img src="public/img/examples/tax-days-icon.png" width="14" height="14" /> <a href="https://apps.apple.com/us/app/tax-days-residency-tracker/id6761441335"><strong>Tax Days</strong></a> (<a href="examples/tax-days">examples</a>)</p>
30
+ <img src="public/img/examples/tax-days.png" alt="Tax Days screenshots" width="100%" />
31
+
32
+ <p><img src="public/img/examples/only-recipes-icon.png" width="14" height="14" /> <a href="https://apps.apple.com/us/app/only-recipes-recipe-keeper/id1553858589"><strong>Only Recipes</strong></a> (<a href="examples/only-recipes">examples</a>)</p>
16
33
  <img src="public/img/examples/only-recipes.png" alt="Only Recipes screenshots" width="100%" />
17
34
 
35
+ <p><img src="public/img/examples/thoughtful-icon.png" width="14" height="14" /> <a href="https://apps.apple.com/us/app/thoughtful-grow-relationships/id6762098726"><strong>Thoughtful</strong></a> (<a href="examples/thoughtful">examples</a>)</p>
36
+ <img src="public/img/examples/thoughtful.png" alt="Thoughtful screenshots" width="100%" />
37
+
18
38
  <p><img src="public/img/examples/cat-iq-test-icon.png" width="14" height="14" /> <a href="https://apps.apple.com/us/app/cat-iq-test/id6759520249"><strong>Cat IQ Test</strong></a> (<a href="examples/cat-iq-test">examples</a>)</p>
19
39
  <img src="public/img/examples/cat-iq-test.png" alt="Cat IQ Test screenshots" width="100%" />
20
40
 
@@ -59,7 +79,7 @@ Then use `oa` anywhere:
59
79
 
60
80
  ```bash
61
81
  oa dev
62
- oa render --force
82
+ oa render
63
83
  oa init
64
84
  ```
65
85
 
@@ -75,6 +95,26 @@ open-assets uses a simple, unified data model. Every asset type follows the same
75
95
  | **Source Size** | The dimensions the HTML template is authored at. Puppeteer scales from source → export size. |
76
96
  | **Output** | An optional post-render action (e.g., write to Xcode `.appiconset`, copy SVG source). |
77
97
 
98
+ ### One template, many sizes, many destinations
99
+
100
+ Every entry in a collection's `export` array is either a size or an output action. A single template renders to all of them in one pass:
101
+
102
+ ```json
103
+ "export": [
104
+ { "name": "iphone-6.9", "label": "iPhone 6.9\"", "size": { "width": 1320, "height": 2868 } },
105
+ { "name": "iphone-6.7", "label": "iPhone 6.7\"", "size": { "width": 1290, "height": 2796 } },
106
+ { "name": "favicon", "size": { "width": 32, "height": 32 }, "outFile": "public/favicon.png" },
107
+ { "type": "xcode", "path": "../MyApp/Assets.xcassets/AppIcon.appiconset" }
108
+ ]
109
+ ```
110
+
111
+ - Sized entries land in `exports/{collection}/{size}/{template}.png`
112
+ - `outFile` sends a rendered size anywhere in your repo (`.png` or `.jpg`), like your web app's `public/` directory
113
+ - `xcode` fills an Xcode `.appiconset` directly
114
+ - `copy-source` copies SVG sources alongside the rendered PNGs
115
+
116
+ Change the template once, run `npx open-assets render`, and every destination updates. Unchanged templates are skipped automatically.
117
+
78
118
  ## End-to-End Screenshot Pipeline
79
119
 
80
120
  ### 1. Initialize
@@ -87,7 +127,16 @@ Creates a `assets.json`, sample HTML templates, and a `public/` directory for sh
87
127
 
88
128
  ### 2. Design with Claude Code
89
129
 
90
- Install the open-assets Claude Code skill into your project:
130
+ The easiest way to get the skill is the Claude Code plugin marketplace. Inside Claude Code, run:
131
+
132
+ ```
133
+ /plugin marketplace add Parra-Inc/open-assets
134
+ /plugin install open-assets@open-assets
135
+ ```
136
+
137
+ The skill is installed once, works in every project, and updates with `/plugin marketplace update open-assets`.
138
+
139
+ Alternatively, copy the skill into a single project:
91
140
 
92
141
  ```bash
93
142
  # Using the built-in command
@@ -123,10 +172,10 @@ Opens a live preview UI at `http://localhost:3200` with zoom/pan controls and ex
123
172
  ### 4. Export
124
173
 
125
174
  ```bash
126
- npx open-assets render --force
175
+ npx open-assets render
127
176
  ```
128
177
 
129
- Exports every template at every configured export size into `./exports/`, organized as `exports/{collection}/{size}/{template}.png`.
178
+ Exports every template at every configured export size into `./exports/`, skipping anything that hasn't changed, organized as `exports/{collection}/{size}/{template}.png`.
130
179
 
131
180
  ### 5. Upload to App Store Connect / Google Play
132
181
 
@@ -141,6 +190,7 @@ Capture real app screenshots via Playwright or XCTest, then use them inside your
141
190
  npx playwright test --project=screenshots
142
191
 
143
192
  # 2. Export marketing screenshots with those captures embedded
193
+ # (--force because captured images aren't checksummed by the cache)
144
194
  npx open-assets render --force
145
195
  ```
146
196
 
@@ -152,10 +202,60 @@ Reference captured screenshots in your HTML templates:
152
202
  The `assets.json` `command` field stores the export command so automation tools know what to run:
153
203
  ```json
154
204
  {
155
- "command": "npx open-assets render --force"
205
+ "command": "npx open-assets render"
156
206
  }
157
207
  ```
158
208
 
209
+ ## Localization
210
+
211
+ Ship screenshots in every language you support, from the same templates. Add a localizations file (an iOS `.xcstrings`-inspired format), reference keys in templates with `{{key}}`, and every template renders once per locale:
212
+
213
+ ```json
214
+ {
215
+ "sourceLanguage": "en",
216
+ "strings": {
217
+ "hero_title": {
218
+ "localizations": {
219
+ "en": { "value": "Track your tax days" },
220
+ "de": { "value": "Verfolge deine Steuertage" },
221
+ "ja": { "value": "税金の日を追跡する" },
222
+ "ar": { "value": "تتبع أيام الضرائب الخاصة بك" }
223
+ }
224
+ }
225
+ }
226
+ }
227
+ ```
228
+
229
+ Wire it to a collection in `assets.json`:
230
+
231
+ ```json
232
+ {
233
+ "id": "screenshots",
234
+ "localizations": "localizations.json",
235
+ "locales": ["en", "de", "ja", "ar"]
236
+ }
237
+ ```
238
+
239
+ Then use the keys in any template:
240
+
241
+ ```html
242
+ <h1>{{hero_title}}</h1>
243
+ ```
244
+
245
+ What you get:
246
+
247
+ - **Per-locale exports**: output paths become `exports/{collection}/{locale}/{size}/{template}.png`, ready for App Store Connect's per-language screenshot slots
248
+ - **RTL support**: right-to-left locales (Arabic, Hebrew, Persian, Urdu) automatically render with `dir="rtl"` set on the document
249
+ - **Locale-aware numbers**: `{{n:1234}}` renders as `1,234` in English and `1.234` in German via `Intl.NumberFormat`
250
+ - **Fallback chain**: exact locale (`es-419`), then base language (`es`), then the source language
251
+
252
+ Render all locales, or just one:
253
+
254
+ ```bash
255
+ npx open-assets render --collection screenshots # every locale
256
+ npx open-assets render --collection screenshots --locale ja # just Japanese
257
+ ```
258
+
159
259
  ## CLI Commands
160
260
 
161
261
  ### `open-assets dev [dir]`
@@ -191,8 +291,8 @@ Options:
191
291
  Render assets headlessly via the command line, without opening a browser.
192
292
 
193
293
  ```bash
194
- open-assets render # Render all collections at source size
195
- open-assets render --force # Export at every size, ignore cache
294
+ open-assets render # Render everything at every export size
295
+ open-assets render --force # Re-render everything, ignoring the cache
196
296
  open-assets render --collection screenshots # Render a specific collection
197
297
  open-assets render --template 01-hero # Render a single template
198
298
  open-assets render --size iphone-6.9 # Use a named export size
@@ -206,28 +306,32 @@ Options:
206
306
  | Flag | Env Var | Default | Description |
207
307
  |------|---------|---------|-------------|
208
308
  | `--collection <id>` | — | all | Render only the collection with this ID |
309
+ | `--tag <tag>` | — | all | Render only collections with this tag |
209
310
  | `--template <name>` | — | all | Render only the template with this name |
210
311
  | `--size <name>` | — | — | Use a named export size from config |
312
+ | `--locale <code>` | — | all | Render only this locale (e.g. `en`, `ar`, `ja`) |
211
313
  | `--width <px>` | — | — | Custom output width |
212
314
  | `--height <px>` | — | — | Custom output height |
213
- | `-f, --force` | — | — | Export at every size and re-render all (ignores cache) |
315
+ | `-f, --force` | — | — | Re-render everything, ignoring the incremental cache |
214
316
  | `-o, --output <dir>` | `OPEN_ASSETS_OUTPUT` | `./exports` | Output directory |
215
317
  | `--config <path>` | `OPEN_ASSETS_CONFIG` | `assets.json` | Path to config file |
216
- | `--parallel <count>` | `OPEN_ASSETS_PARALLEL` | `1` | Number of parallel renders |
217
318
  | `--render-timeout <ms>` | `OPEN_ASSETS_RENDER_TIMEOUT` | `30000` | Puppeteer render timeout |
218
319
  | `--json` | — | — | Output results as JSON |
219
320
  | `-q, --quiet` | `OPEN_ASSETS_QUIET` | `false` | Suppress progress logs |
220
321
 
221
- **Selective export** — flags compose naturally:
322
+ **Selective export**: flags compose naturally:
222
323
  ```bash
223
324
  # Single template at a single device size
224
325
  open-assets render --collection screenshots --template 01-hero --size iphone-6.9
225
326
 
226
327
  # One template at all device sizes
227
- open-assets render --collection screenshots --template 01-hero --force
328
+ open-assets render --collection screenshots --template 01-hero
228
329
 
229
330
  # All templates at one device size
230
331
  open-assets render --collection screenshots --size iphone-6.9
332
+
333
+ # All templates in one language
334
+ open-assets render --collection screenshots --locale ja
231
335
  ```
232
336
 
233
337
  **Output structure**: `exports/{collection}/{size}/{template}.png`
@@ -272,7 +376,7 @@ open-assets validate # Check current directory
272
376
  open-assets validate ./assets # Check specific directory
273
377
  ```
274
378
 
275
- Returns exit code 1 if any errors are found — useful in CI pipelines.
379
+ Returns exit code 1 if any errors are found, useful in CI pipelines.
276
380
 
277
381
  ### `open-assets skills [dir]`
278
382
 
@@ -286,6 +390,13 @@ open-assets skills ./my-app # Install to a specific project
286
390
  npx skills add https://github.com/Parra-Inc/open-assets --skill open-assets
287
391
  ```
288
392
 
393
+ Prefer a global, per-user install? Use the Claude Code plugin marketplace instead: the skill then works in every project without copying files:
394
+
395
+ ```
396
+ /plugin marketplace add Parra-Inc/open-assets
397
+ /plugin install open-assets@open-assets
398
+ ```
399
+
289
400
  ### `open-assets init [dir]`
290
401
 
291
402
  Scaffold a new project with a `assets.json`, sample HTML templates, and a `public/` directory.
@@ -304,7 +415,7 @@ Your project needs a `assets.json` at its root. This file defines the collection
304
415
  "version": 1,
305
416
  "name": "My App",
306
417
  "publicDir": "public",
307
- "command": "npx open-assets render --force",
418
+ "command": "npx open-assets render",
308
419
  "collections": [ ... ]
309
420
  }
310
421
  ```
@@ -317,6 +428,7 @@ Your project needs a `assets.json` at its root. This file defines the collection
317
428
  | `name` | string | yes | App display name |
318
429
  | `publicDir` | string | no | Directory for shared assets (auto-served by dev server) |
319
430
  | `command` | string | no | Export command for automation tools and CI |
431
+ | `tags` | array | no | Tag definitions (`{ id, label }`) for grouping collections |
320
432
  | `collections` | array | yes | Asset collection definitions |
321
433
 
322
434
  ### Collection Schema (Unified)
@@ -345,8 +457,11 @@ All collections follow the same structure. No `type` field needed.
345
457
  |-------|------|-------------|
346
458
  | `id` | string | Unique collection identifier (used in CLI `--collection`) |
347
459
  | `label` | string | Display name in the collection selector |
348
- | `sourceSize` | object | `{ width, height }` — the dimensions templates are authored at |
460
+ | `tags` | array | Tag ids applied to this collection (filter renders with `--tag`) |
461
+ | `sourceSize` | object | `{ width, height }`: the dimensions templates are authored at |
349
462
  | `borderRadius` | number | Border radius for preview frames (px) |
463
+ | `localizations` | string | Path to a localizations JSON file (see [Localization](#localization)) |
464
+ | `locales` | array | Optional locale filter. Omit to render every locale in the localizations file |
350
465
  | `templates` | array | Source files to render |
351
466
  | `templates[].src` | string | Path to HTML/SVG file (relative to project root) |
352
467
  | `templates[].name` | string | Filename for exports (used by `--template` flag) |
@@ -354,14 +469,14 @@ All collections follow the same structure. No `type` field needed.
354
469
  | `export` | array | Array of export sizes and output actions |
355
470
  | `export[].name` | string | Size identifier (used by `--size` flag) |
356
471
  | `export[].label` | string | Display label |
357
- | `export[].size` | object | `{ width, height }` — output dimensions in pixels |
472
+ | `export[].size` | object | `{ width, height }`: output dimensions in pixels |
358
473
  | `export[].outFile` | string | Optional output path for this size (supports .png, .jpg) |
359
474
  | `export[].type` | string | Output action type (`xcode`, `copy-source`). Entries with `type` are post-render actions, not sizes |
360
475
  | `customExport` | object | Optional `{ defaultWidth, defaultHeight }` for custom size UI |
361
476
 
362
477
  ### Output Types
363
478
 
364
- Export entries with a `type` field are post-render actions (run with `--force`):
479
+ Export entries with a `type` field are post-render actions that run after every render:
365
480
 
366
481
  | Type | Config | Description |
367
482
  |------|--------|-------------|
@@ -487,7 +602,7 @@ Export entries with a `type` field are post-render actions (run with `--force`):
487
602
  The `render` command maintains a `assets.lock` file that stores SHA256 checksums of each source HTML/SVG file. On subsequent renders, unchanged assets are skipped automatically:
488
603
 
489
604
  ```
490
- $ open-assets render --force
605
+ $ open-assets render
491
606
  Skipping 01-hero at 1320x2868 (unchanged)
492
607
  Rendering 03-new-screen at 1320x2868...
493
608
  → exports/screenshots/iphone-6.9/03-new-screen.png
@@ -497,7 +612,7 @@ Done. 1 asset(s) rendered, 1 skipped (unchanged) in 1.2s.
497
612
 
498
613
  Use `--force` to re-render everything regardless of the cache.
499
614
 
500
- **Limitation**: Only source HTML/SVG files are checksummed. Changes to referenced assets (images in `publicDir`, compiled Tailwind CSS) won't trigger re-renders — use `--force` when those change.
615
+ **Limitation**: Only source HTML/SVG files are checksummed. Changes to referenced assets (images in `publicDir`, compiled Tailwind CSS) won't trigger re-renders, so use `--force` when those change.
501
616
 
502
617
  Commit `assets.lock` to your repository so teammates and CI benefit from the cache.
503
618
 
@@ -522,7 +637,7 @@ jobs:
522
637
  node-version: 20
523
638
  - run: npm ci
524
639
  - run: npx open-assets validate
525
- - run: npx open-assets render --force --json --quiet
640
+ - run: npx open-assets render --json --quiet
526
641
  - uses: actions/upload-artifact@v4
527
642
  with:
528
643
  name: screenshots
@@ -550,6 +665,7 @@ jobs:
550
665
  project/
551
666
  assets.json
552
667
  assets.lock # Auto-generated cache (commit this)
668
+ localizations.json # Localized strings (optional)
553
669
  public/ # Shared assets (configured via publicDir)
554
670
  logo-round.png
555
671
  social/
@@ -570,7 +686,7 @@ project/
570
686
  default.html
571
687
  dist/
572
688
  styles.css # Compiled Tailwind
573
- exports/ # Rendered output (gitignored)
689
+ exports/ # Rendered output (commit this)
574
690
  screenshots/
575
691
  iphone-6.9/
576
692
  01-hero.png
@@ -92,10 +92,9 @@ program
92
92
  .option("--locale <code>", "Render only this locale (e.g. en, ar, ja)")
93
93
  .option("--width <px>", "Output width in pixels (custom size)")
94
94
  .option("--height <px>", "Output height in pixels (custom size)")
95
- .option("-f, --force", "Export at every size and re-render all assets (ignores cache)")
95
+ .option("-f, --force", "Re-render all assets, ignoring the incremental cache")
96
96
  .option("-o, --output <dir>", "Output directory (env: OPEN_ASSETS_OUTPUT)", env("OPEN_ASSETS_OUTPUT", "./exports"))
97
97
  .option("--config <path>", "Path to config file (env: OPEN_ASSETS_CONFIG)", env("OPEN_ASSETS_CONFIG", "assets.json"))
98
- .option("--parallel <count>", "Number of parallel renders (env: OPEN_ASSETS_PARALLEL)", env("OPEN_ASSETS_PARALLEL", "1"))
99
98
  .option("--render-timeout <ms>", "Puppeteer render timeout in ms", env("OPEN_ASSETS_RENDER_TIMEOUT", "30000"))
100
99
  .option("--json", "Output results as JSON (for CI/scripting)")
101
100
  .option("-q, --quiet", "Suppress progress logs", env("OPEN_ASSETS_QUIET") === "true")
@@ -116,7 +115,7 @@ program
116
115
 
117
116
  const log = opts.quiet ? undefined : (msg) => console.log(msg);
118
117
  const deps = {
119
- renderScreenshot: renderer.renderScreenshot,
118
+ renderVariants: renderer.renderVariants,
120
119
  runXcodeOutput: renderer.runXcodeOutput,
121
120
  closeBrowser: renderer.closeBrowser,
122
121
  readLockfile: lockfile.readLockfile,
package/lib/init.mjs CHANGED
@@ -632,7 +632,7 @@ export async function scaffoldProject(dir) {
632
632
  version: 1,
633
633
  name: `${projectName} Assets`,
634
634
  publicDir: "public",
635
- command: "npx open-assets render --all",
635
+ command: "npx open-assets render",
636
636
  tags,
637
637
  collections,
638
638
  };
package/lib/lockfile.mjs CHANGED
@@ -91,14 +91,17 @@ export function computeChecksum(filePath) {
91
91
 
92
92
  /**
93
93
  * Check if an asset variant is up-to-date based on its stored checksums.
94
- * Returns true if both source and export config checksums match and the output file exists.
94
+ * Returns true if the source and export config checksums match, the entry was
95
+ * recorded for the same output path, and the output file exists.
96
+ * @param {string} [projectDir] - Base directory for resolving relative output paths
95
97
  */
96
- export function isUpToDate(lockData, assetKey, variantKey, currentChecksum, outputPath, exportChecksum) {
98
+ export function isUpToDate(lockData, assetKey, variantKey, currentChecksum, outputPath, exportChecksum, projectDir = ".") {
97
99
  const entry = lockData.assets?.[assetKey]?.[variantKey];
98
100
  if (!entry) return false;
99
101
  if (entry.sourceChecksum !== currentChecksum) return false;
100
102
  if (exportChecksum && entry.exportChecksum !== exportChecksum) return false;
101
- if (!existsSync(resolve(outputPath))) return false;
103
+ if (entry.outputPath !== outputPath) return false;
104
+ if (!existsSync(resolve(projectDir, outputPath))) return false;
102
105
  return true;
103
106
  }
104
107
 
package/lib/manifest.mjs CHANGED
@@ -83,14 +83,13 @@ export function filterTemplates(templates, templateName) {
83
83
 
84
84
  /**
85
85
  * Determine which sizes to render for a collection given CLI options.
86
+ * By default every configured export size is rendered; --width/--height or
87
+ * --size narrow the selection.
86
88
  * @param {object} collection - The collection from the config
87
- * @param {{ width?: string|number, height?: string|number, size?: string, force?: boolean, platform?: string }} opts
89
+ * @param {{ width?: string|number, height?: string|number, size?: string, platform?: string }} opts
88
90
  * @returns {{ sizes: object[], warning?: string }}
89
91
  */
90
92
  export function resolveRenderSizes(collection, opts = {}) {
91
- const sourceW = collection.sourceSize.width;
92
- const sourceH = collection.sourceSize.height;
93
-
94
93
  if (opts.width && opts.height) {
95
94
  const w = typeof opts.width === "number" ? opts.width : parseInt(opts.width);
96
95
  const h = typeof opts.height === "number" ? opts.height : parseInt(opts.height);
@@ -116,17 +115,6 @@ export function resolveRenderSizes(collection, opts = {}) {
116
115
  };
117
116
  }
118
117
 
119
- if (opts.force) {
120
- const sizes = flatSizes(collection);
121
- return { sizes };
122
- }
123
-
124
- // Default: render at source size
125
- return {
126
- sizes: [{
127
- name: `${sourceW}x${sourceH}`,
128
- label: `${sourceW}x${sourceH}`,
129
- size: { width: sourceW, height: sourceH },
130
- }],
131
- };
118
+ // Default: render at every configured export size
119
+ return { sizes: flatSizes(collection) };
132
120
  }