WORK-457
ID:WORK-457Status:done

JSON preset carrier: loader extension detection

SPEC-111 §6 — presets are pure data, so support a declarative .json carrier (the default for new packs) alongside JS/TS modules in the preset loader.

Priority:mediumComplexity:simpleMilestone:v0.25.0Source:SPEC-111

Criteria completion

Criteria completion: 5 of 5 (100%) checked; history from Jun 23 to Jun 240%25%50%75%100%Jun 23Jun 24
Branches 3
History 3
  1. f6e6a38
    • ☑ The preset loader (`packages/transform/src/preset-loader.ts`) detects a `.json` `module` by extension and reads it (`readFile` + `JSON.parse`) instead of `import()`-ing it
    • ☑ Both carriers yield the same `ThemeTokensConfig` and satisfy the loader's existing "resolved export is a plain object" guard
    • ☑ Lumina's `.ts` presets keep working unchanged
    • ☑ A JSON preset requires no build step — its `module` points at the `.json` directly
    • ☑ Tests cover JSON and JS/TS resolution plus a malformed-JSON error
    by bjornolofandersson
  2. dede57e
    Created (ready)by bjornolofandersson
  3. a6bdcba
    Content editedby Claude
    plan(v0.25.0): accept specs/ADRs and decompose into 21 work items

Acceptance Criteria

  • The preset loader (packages/transform/src/preset-loader.ts) detects a .json module by extension and reads it (readFile + JSON.parse) instead of import()-ing it
  • Both carriers yield the same ThemeTokensConfig and satisfy the loader's existing "resolved export is a plain object" guard
  • Lumina's .ts presets keep working unchanged
  • A JSON preset requires no build step — its module points at the .json directly
  • Tests cover JSON and JS/TS resolution plus a malformed-JSON error

Approach

Add an extension check at the top of the loader's resolve path; everything downstream (merge, scope filter) is unchanged.

Dependencies

  • WORK-456 — the pack format / module field

References

  • SPEC-111 §6; packages/transform/src/preset-loader.ts

Resolution

Completed: 2026-06-23

Branch: claude/v0.25.0-impl

What was done

  • packages/transform/src/preset-loader.tsloadPreset now branches on a .json resolved specifier: reads the file and JSON.parses it (via fileURLToPath + readFileSync) instead of import(). Works for relative, absolute, and package-resolved .json paths. JS/TS modules keep the default/config import path unchanged. Both carriers hit the same plain-object guard.
  • Fixtures preset-syntax.json (valid) + preset-not-object.json; 2 new tests in preset-loader.test.ts (9 total, passing).

Notes

  • No build step needed for a JSON preset — its module points straight at the .json. This is the carrier WORK-448 scaffolds by default and the basis for the editor story (WORK-458 JSON Schema).