From 635bc2a95260be4e373fc602a3f217610f3b51b9 Mon Sep 17 00:00:00 2001 From: David Montero Crespo Date: Thu, 16 Apr 2026 01:02:27 -0300 Subject: [PATCH] fix(examples): merge circuitExamples via array spread, not side-effect push Previously examples.ts pushed to exampleProjects[] after declaration, which some bundlers can treat as dead code under aggressive tree-shaking. This also made HMR unreliable when examples-circuits.ts changed. Now: const legacyExamples = [...] export const exampleProjects = [...legacyExamples, ...circuitExamples] Single immutable export. Guaranteed to include all 150 examples at import time. The gallery (ExamplesGallery.tsx) and SSR prerender (entry-server.tsx) both pick up the new circuit examples automatically. Also adds frontend/src/__tests__/examples-circuits.test.ts with 5 assertions to catch future regressions (all circuit ids present, no duplicates, valid required fields, expected categories covered). Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/__tests__/examples-circuits.test.ts | 45 +++++++++++++++++++ frontend/src/data/examples.ts | 11 +++-- 2 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 frontend/src/__tests__/examples-circuits.test.ts diff --git a/frontend/src/__tests__/examples-circuits.test.ts b/frontend/src/__tests__/examples-circuits.test.ts new file mode 100644 index 00000000..89f6ae2b --- /dev/null +++ b/frontend/src/__tests__/examples-circuits.test.ts @@ -0,0 +1,45 @@ +/** + * Ensures that circuitExamples (examples-circuits.ts) are merged into + * exampleProjects so the /examples gallery and SSR prerender include them. + */ +import { describe, it, expect } from 'vitest'; +import { exampleProjects } from '../data/examples'; +import { circuitExamples } from '../data/examples-circuits'; + +describe('circuitExamples integration', () => { + it('circuitExamples has at least 30 new examples', () => { + expect(circuitExamples.length).toBeGreaterThanOrEqual(30); + }); + + it('every circuitExample id appears in exampleProjects', () => { + const allIds = new Set(exampleProjects.map((e) => e.id)); + const missing = circuitExamples.map((e) => e.id).filter((id) => !allIds.has(id)); + expect(missing).toEqual([]); + }); + + it('no duplicate ids in exampleProjects', () => { + const ids = exampleProjects.map((e) => e.id); + const dupes = ids.filter((id, i) => ids.indexOf(id) !== i); + expect(dupes).toEqual([]); + }); + + it('circuit examples cover all expected categories', () => { + const cats = new Set(circuitExamples.map((e) => e.category)); + expect(cats.has('basics')).toBe(true); + expect(cats.has('sensors')).toBe(true); + expect(cats.has('robotics')).toBe(true); + }); + + it('every example has valid required fields', () => { + for (const e of circuitExamples) { + expect(e.id, `missing id: ${JSON.stringify(e)}`).toBeTypeOf('string'); + expect(e.title).toBeTypeOf('string'); + expect(e.description).toBeTypeOf('string'); + expect(e.category).toMatch(/^(basics|sensors|displays|communication|games|robotics)$/); + expect(e.difficulty).toMatch(/^(beginner|intermediate|advanced)$/); + expect(e.code).toBeTypeOf('string'); + expect(Array.isArray(e.components)).toBe(true); + expect(Array.isArray(e.wires)).toBe(true); + } + }); +}); diff --git a/frontend/src/data/examples.ts b/frontend/src/data/examples.ts index e828bfd5..b07f0983 100644 --- a/frontend/src/data/examples.ts +++ b/frontend/src/data/examples.ts @@ -4,6 +4,8 @@ * Collection of example projects that users can load and run */ +import { circuitExamples } from './examples-circuits'; + /** Per-board setup for multi-board examples */ export interface ExampleBoard { /** Must match a BoardKind — determines the board instance ID (first board of a kind → boardKind string) */ @@ -52,7 +54,7 @@ export interface ExampleProject { libraries?: string[]; } -export const exampleProjects: ExampleProject[] = [ +const legacyExamples: ExampleProject[] = [ { id: 'blink-led', title: 'Blink LED', @@ -4799,9 +4801,10 @@ void loop() { }, ]; -// Append circuit-focused examples (analog, digital gates, electromechanical) -import { circuitExamples } from './examples-circuits'; -exampleProjects.push(...circuitExamples); +// Merge legacy examples with circuit-focused examples (analog, digital gates, +// electromechanical). Declared after both arrays exist so the export is a +// single immutable value — safe from tree-shaking quirks. +export const exampleProjects: ExampleProject[] = [...legacyExamples, ...circuitExamples]; // Get examples by category export function getExamplesByCategory(category: ExampleProject['category']): ExampleProject[] {