Repository navigation
Refactor/registry split - #113
Merged
Merged
Conversation
ObjectType.ts was a 451-LOC catch-all mixing five concerns. Split into: - typeHelpers.ts: makeEnumGuard, intInRange (shared helpers) - LabelConfig.ts: labelConfigSchema + MEDIA/PRINT/SPEED/DARKNESS/LABEL_LENGTH enums and ranges - PrinterProfile.ts: extended with PRINTER_LOCALE/ZPL_MODE/CLOCK_* enums, setup-script regex - LabelObject.ts: labelObjectBaseSchema + ObjectChanges + ObjectGroup - ZplEmit.ts: ZplEmitContext, HriBehavior, TransformContext ObjectType.ts now holds only ObjectTypeDefinition (the registry shape). Mechanical import rewrites via scripts/split_object_type.local.py touched 38 files. Domain symbols now import from their semantic home; ObjectType.ts no longer doubles as a barrel for unrelated schemas. No behavior change: 1412 tests pass, lint and build green.
…lot box Splits the registry entry type into a React-free Core (toZPL, commitTransform, normalizeChanges, defaults, locks) and a Ui half (PropertiesPanel only). Legacy ObjectTypeDefinition stays as the intersection alias during migration. src/registry/index.ts now ships two key-parallel maps: ObjectRegistry (Core) for the domain path (zplGenerator, parser handlers) and ObjectPanels (Ui) for components. Bundled .tsx entries narrow into both maps by structural typing; split entries (box) import each half from its own file. box.tsx → box.ts + box.panel.tsx as the migration pilot. Pattern: - <type>.ts: Props interface + ObjectTypeCore<P> (no React imports) - <type>.panel.tsx: ObjectTypeUi<P> with the PropertiesPanel component PropertiesPanel.tsx reads the per-type panel from ObjectPanels instead of from the ObjectRegistry entry. registry-isolation.test.ts gains a key-parity check between the two maps and a per-split-entry assertion that no PropertiesPanel leaks onto Core. 1414 tests pass (+ 2 parity tests).
Breaks the types/Group -> registry runtime cycle that previously made any consumer of LabelObject transitively import all 34 .tsx registry files. LeafObject + Leaf<T,P> moved into src/registry/leafObject.ts: pure type declarations, no value exports, no runtime presence. types/Group.ts now imports LeafObject from there; registry/index.ts keeps a back-compat re-export for the 5 Canvas callers. The remaining text.tsx + line.tsx -> labelStore runtime cycle resolves naturally as Stages 3-5 split each .tsx into .ts + .panel.tsx (the .ts half holds no React or store dependency). 1414 tests pass, lint and build green.
createBarcode1D split into: - barcode1d.ts: createBarcode1DCore + Barcode1DProps + Barcode1DConfig + BarcodeLocale - barcode1d.panel.tsx: createBarcode1DPanel (PropertiesPanel component factory) Each of the 18 entries that called createBarcode1D now ships as a pair: - <name>.ts: exports <name>Config: Barcode1DConfig + <name> = createBarcode1DCore(<name>Config) - <name>.panel.tsx: exports <name>Panel = createBarcode1DPanel(<name>Config) Mechanical split via scripts/split_barcode1d_entries.local.py. src/registry/index.ts: imports each split entry's .ts for ObjectRegistry and .panel.tsx for ObjectPanels. The 16 still-bundled .tsx entries (text, qrcode, datamatrix, ellipse, line, serial, image, gs1databar, pdf417, aztec, micropdf417, codablock, code49, maxicode, symbol) continue to feed both maps via structural typing. src/registry/leafObject.ts: Props imports for split entries switched from .tsx to bare specifier (resolves to .ts). registry-isolation.test.ts split-types derivation now filters factory files (barcode1d.panel.tsx) by intersecting with ObjectRegistry keys. 1414 tests pass, lint and build green.
Split each of aztec, codablock, code49, datamatrix, ellipse, gs1databar, image, line, micropdf417, pdf417, qrcode into <name>.ts + <name>.panel.tsx. Pattern follows box.ts/box.panel.tsx: Core file holds props interface, helpers/constants and the ObjectTypeCore entry (no React imports); panel file holds the ObjectTypeUi with the PropertiesPanel JSX, importing constants from the Core file when needed. Special exports preserved on the Core side: - line.ts: pickAngle helper - code49.ts: Code49Mode type - Previously-private constants used by the panel (MAGNIFICATION_MIN/MAX, EC_LEVEL_MIN/MAX, DIMENSION_MIN/MAX, CODE49_MODES, SYMBOLOGY_LABELS, code49Min/MaxHeight) got an explicit export so the panel can import them. src/registry/index.ts: imports each split entry's .ts for ObjectRegistry and .panel.tsx for ObjectPanels. 4 entries remain bundled (text, serial, maxicode, symbol) for Stage 5. src/registry/leafObject.ts: bare-specifier imports for split entries. 1414 tests pass, lint and build green.
…ries done
Final 4 bundled entries split into Core (.ts) + Ui (.panel.tsx) pairs.
Special exports preserved on Core:
- text.ts: none new
- serial.ts: serialSpec (shared between toZPL filter and panel input filter)
- maxicode.ts: ALL_MODES (panel-only, exported per Stage 5 pattern)
- symbol.ts: GS_SYMBOLS, SymbolCode, DEFAULT_GS_SYMBOL, DEFAULT_GS_SYMBOL_META,
GS_SYMBOL_CODES (all consumed by parser/canvas)
text.ts is now React/store-free: drops useLabelStore, useT, hooks, components,
and React imports — all moved into text.panel.tsx. This breaks the long-standing
text.tsx -> labelStore -> registry/index.ts runtime cycle on the Core side.
Verified: zero src/registry/*.ts files import from store/, hooks/, React, or
lib/useT. The runtime cycle that survived through Stages 3+4 (because text.tsx
and line.tsx still pulled labelStore) is now fully resolved on the registry
Core path.
1414 tests pass, lint and build green. All 34 entries shipped as <name>.ts +
<name>.panel.tsx; the structural-narrowing shim in index.ts no longer relies
on any bundled .tsx entry.
Stage 6 (next): split ObjectPanels into a separate panels.ts barrel so
src/lib/zplGenerator.ts can import ObjectRegistry without pulling any panel
file (the headline architecture goal of the refactor).
…panels.ts)
The headline architecture goal of the registry-split refactor.
Before: src/registry/index.ts imported all 34 .panel.tsx files to bundle
ObjectPanels alongside ObjectRegistry. lib/zplGenerator.ts and
lib/zplParser/** transitively pulled React + Konva + components into the
emit/parse path even though they never rendered.
After:
- src/registry/index.ts holds only ObjectRegistry (Core; React-free).
- src/registry/panels.ts holds ObjectPanels (Ui); imported only by
PropertiesPanel.tsx (and the registry-isolation test).
PropertiesPanel.tsx switches to:
import { ObjectRegistry } from '../../registry';
import { ObjectPanels } from '../../registry/panels';
registry-isolation.test.ts now asserts the strong property: every Core
entry has no PropertiesPanel field (drops the file-glob-derived
splitTypes workaround that protected this during the Stage 3-5 migration).
Verified:
- grep src/lib/** finds zero references to panels or .panel
- grep src/registry/*.ts finds zero React or registry/panels imports
- 1414 tests pass, lint and build green
…try/ObjectPanels
Adds typed map shapes derived from the LeafObject discriminated union:
- ObjectRegistryMap = { [T in LeafType]: ObjectTypeCore<PropsFor<T>> }
- ObjectPanelsMap = { [T in LeafType]: ObjectTypeUi<PropsFor<T>> }
Both live in src/registry/leafObject.ts (the type-only module Stages 2.5).
The ObjectRegistry and ObjectPanels literals in src/registry/index.ts and
src/registry/panels.ts now use 'satisfies ObjectRegistryMap' / 'satisfies
ObjectPanelsMap'. Effect: adding a new entry to LeafObject without a
matching registry entry fails the build with TS1360 — verified by an
exhaustiveness probe (temp-comment out 'serial' triggered the expected
error, restored).
External API unchanged: both maps still EXPORT as Record<string, ...<any>>
to keep the .toZPL union dispatch and 'group' lookups working without
consumer churn. The Stage 7 win is the build-time exhaustiveness check
on the literal, not per-key narrowing in callers.
Barcode1DConfig core/panel split deferred: would duplicate shared fields
(heightLocked, interpretationLocked) for marginal type-graph cleanup.
TODO marker stays.
1414 tests pass, lint and build green.
Barcode1DConfig was a unified shape consumed by both factories;
createBarcode1DCore ignored its UI-only fields (locale, hasCheckDigit,
contentSpec) and createBarcode1DPanel ignored its Core-only fields.
Split into:
- Barcode1DCoreConfig in barcode1d.ts (label, icon, group, defaultContent,
byRatio?, heightLocked?, interpretationLocked?, hri?, zplCommand)
- Barcode1DPanelConfig in barcode1d.panel.tsx (locale, hasCheckDigit,
contentSpec?, heightLocked?, interpretationLocked?)
The BarcodeLocale interface moves with its only consumer to barcode1d.panel.tsx.
Effect on the Core type graph: barcode1d.ts no longer imports Translations
(../locales) or ContentSpec (./contentSpec) — both were only there to type
the panel-only fields.
Each of the 18 factory entries:
- <name>.ts: <name>Config -> <name>CoreConfig; drops locale,
hasCheckDigit, contentSpec lines
- <name>.panel.tsx: drops the <name>Config import; inlines the panel
config directly into createBarcode1DPanel({ ... })
The TODO marker added in Stage 4 is gone — the deferral assumed shared
fields (heightLocked, interpretationLocked) would cause duplication, but
none of the 18 factory entries actually sets them; only gs1databar and
maxicode (both non-factory) do.
1414 tests pass, lint and build green.
…eafType Promotes the registry export from Record<string, ObjectTypeCore<any>> to Record<LeafType, ObjectTypeCore<any>>. Literal-key access (e.g. ObjectRegistry.code128 or ObjectRegistry['code128']) now catches typos at compile time — ObjectRegistry['code218'] is a TS error instead of silently returning undefined. Dynamic lookups over LabelObject['type'] (which includes 'group') would break this contract, so add two helpers that keep the permissive surface where it's actually needed: getEntry(type: string): ObjectTypeCore<any> | undefined // index.ts getPanel(type: string): ObjectTypeUi<any> | undefined // panels.ts Both wrap the typed map with an internal Record<string, ...|undefined> cast so 'group' / unknown strings return undefined as before. Migrated 7 consumer call sites to getEntry / getPanel: - src/components/Canvas/hooks/useKonvaTransformer.ts (3 sites) - src/components/Canvas/LabelCanvas.tsx (drag-drop type) - src/components/Properties/LayerRow.tsx - src/components/Properties/PropertiesPanel.tsx (entry + panel) - src/store/labelStore.ts (normalizeChanges + addObject) - src/lib/zplGenerator.ts (toZPL emit) - src/registry/registry.test.ts (2 dynamic-typed sites) Test sites using literal keys (ObjectRegistry['box'] etc.) stay unchanged and now benefit from the typo-catch. Also exports LeafType from src/registry/index.ts as a convenience re-export. 1414 tests pass, lint and build green.
Final review pass cleanups:
- getEntry / getPanel bodies switched from redeclaring ObjectTypeCore<any>
/ ObjectTypeUi<any> to (typeof ObjectRegistry)[LeafType] /
(typeof ObjectPanels)[LeafType]. Removes 2x <any> declarations and
2x eslint-disable-next-line lines; the public maps remain the single
source of the permissive type.
- JSDoc on ObjectRegistry, ObjectPanels, getEntry, getPanel trimmed to
CLAUDE.md terseness ("kurz und prägnant"). Single-line headers where
the signature already tells the story.
1414 tests pass, lint and build green. Three full-branch review rounds
(Architecture / Code+Smells / Behavior / Comments) closed with zero
findings across all four lanes.
There was a problem hiding this comment.
Code Review
This pull request refactors the object registry and type definitions to isolate React-dependent UI components (such as properties panels) from core domain logic and ZPL generation, preventing React imports from leaking into non-UI modules. Two issues were identified in the review: a critical runtime ReferenceError in micropdf417.panel.tsx due to an undefined loc variable, and a script inconsistency in the Serbian locale file (sr.ts) where the translation for mode was written in Latin instead of Cyrillic.
Locale-key fix from the micropdf417-mode addition landed 'Režim' (Latin) in the Serbian Cyrillic locale, inconsistent with the sibling keys (Садржај / Висина реда / Ширина модула). Switch to 'Режим'. Other 'Režim' occurrences in sr.ts (code49, maxicode blocks) are pre-existing complete-Latin blocks — internally consistent, out of scope.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.