@@ -10,12 +10,12 @@ function sortAllFilenames(files: File[], filenamesSorted: string[]): File[] {
1010 ] . filter ( Boolean ) as File [ ] ;
1111}
1212
13- export interface Framework {
13+ /**
14+ * A specific released version of a framework, e.g. "Svelte 5" or "Vue 3".
15+ */
16+ export interface FrameworkVersion {
1417 id : string ;
1518 title : string ;
16- frameworkName : string ;
17- frameworkNameId : string ;
18- isLatestStable : boolean ;
1919 img : string ;
2020 playgroundURL : string ;
2121 documentationURL : string ;
@@ -25,13 +25,22 @@ export interface Framework {
2525 releaseDate : string ;
2626}
2727
28- export const frameworks : Framework [ ] = [
28+ /**
29+ * A framework family, e.g. "Svelte". The generic name (`id`) resolves to the
30+ * version referenced by `latestStable`, which is the single source of truth for
31+ * "which version is the current stable one" — making multiple-stable states
32+ * unrepresentable.
33+ */
34+ export interface Framework {
35+ id : string ;
36+ name : string ;
37+ latestStable : string ;
38+ }
39+
40+ export const frameworkVersions : FrameworkVersion [ ] = [
2941 {
3042 id : "svelte5" ,
3143 title : "Svelte 5" ,
32- frameworkName : "Svelte" ,
33- frameworkNameId : "svelte" ,
34- isLatestStable : false ,
3544 img : "framework/svelte.svg" ,
3645 playgroundURL : "https://svelte.dev/playground" ,
3746 documentationURL : "https://svelte.dev" ,
@@ -45,9 +54,6 @@ export const frameworks: Framework[] = [
4554 {
4655 id : "react" ,
4756 title : "React" ,
48- frameworkName : "React" ,
49- frameworkNameId : "react" ,
50- isLatestStable : true ,
5157 img : "framework/react.svg" ,
5258 playgroundURL : "https://codesandbox.io/s/mystifying-goldberg-6wx04b" ,
5359 documentationURL : "https://reactjs.org" ,
@@ -61,9 +67,6 @@ export const frameworks: Framework[] = [
6167 {
6268 id : "vue3" ,
6369 title : "Vue 3" ,
64- frameworkName : "Vue" ,
65- frameworkNameId : "vue" ,
66- isLatestStable : true ,
6770 img : "framework/vue.svg" ,
6871 playgroundURL : "https://play.vuejs.org/" ,
6972 documentationURL : "https://vuejs.org/guide" ,
@@ -77,9 +80,6 @@ export const frameworks: Framework[] = [
7780 {
7881 id : "angularRenaissance" ,
7982 title : "Angular Renaissance" ,
80- frameworkName : "Angular" ,
81- frameworkNameId : "angular" ,
82- isLatestStable : true ,
8383 img : "framework/angular-renaissance.svg" ,
8484 playgroundURL : "https://codesandbox.io/s/angular" ,
8585 documentationURL : "https://angular.io/docs" ,
@@ -98,9 +98,6 @@ export const frameworks: Framework[] = [
9898 {
9999 id : "angular" ,
100100 title : "Angular" ,
101- frameworkName : "Angular" ,
102- frameworkNameId : "angular" ,
103- isLatestStable : false ,
104101 img : "framework/angular.svg" ,
105102 playgroundURL : "https://codesandbox.io/s/angular" ,
106103 documentationURL : "https://angular.io/docs" ,
@@ -119,9 +116,6 @@ export const frameworks: Framework[] = [
119116 {
120117 id : "lit" ,
121118 title : "Lit" ,
122- frameworkName : "Lit" ,
123- frameworkNameId : "lit" ,
124- isLatestStable : true ,
125119 img : "framework/lit.svg" ,
126120 playgroundURL : "https://lit.dev/playground" ,
127121 documentationURL : "https://lit.dev" ,
@@ -135,9 +129,6 @@ export const frameworks: Framework[] = [
135129 {
136130 id : "emberPolaris" ,
137131 title : "Ember Polaris" ,
138- frameworkName : "Ember" ,
139- frameworkNameId : "ember" ,
140- isLatestStable : false ,
141132 img : "framework/ember.svg" ,
142133 playgroundURL : "http://new.emberjs.com" ,
143134 documentationURL : "https://emberjs.com" ,
@@ -151,9 +142,6 @@ export const frameworks: Framework[] = [
151142 {
152143 id : "emberOctane" ,
153144 title : "Ember Octane" ,
154- frameworkName : "Ember" ,
155- frameworkNameId : "ember" ,
156- isLatestStable : true ,
157145 img : "framework/ember.svg" ,
158146 playgroundURL : "https://ember-twiddle.com" ,
159147 documentationURL : "https://emberjs.com" ,
@@ -167,9 +155,6 @@ export const frameworks: Framework[] = [
167155 {
168156 id : "solid" ,
169157 title : "Solid.js" ,
170- frameworkName : "Solid" ,
171- frameworkNameId : "solid" ,
172- isLatestStable : true ,
173158 img : "framework/solid.svg" ,
174159 playgroundURL : "https://playground.solidjs.com/" ,
175160 documentationURL : "https://www.solidjs.com/" ,
@@ -183,9 +168,6 @@ export const frameworks: Framework[] = [
183168 {
184169 id : "svelte4" ,
185170 title : "Svelte 4" ,
186- frameworkName : "Svelte" ,
187- frameworkNameId : "svelte" ,
188- isLatestStable : true ,
189171 img : "framework/svelte.svg" ,
190172 playgroundURL : "https://svelte.dev/repl" ,
191173 documentationURL : "https://svelte.dev/" ,
@@ -199,9 +181,6 @@ export const frameworks: Framework[] = [
199181 {
200182 id : "vue2" ,
201183 title : "Vue 2" ,
202- frameworkName : "Vue" ,
203- frameworkNameId : "vue" ,
204- isLatestStable : false ,
205184 img : "framework/vue.svg" ,
206185 playgroundURL : "" ,
207186 documentationURL : "https://v2.vuejs.org" ,
@@ -215,9 +194,6 @@ export const frameworks: Framework[] = [
215194 {
216195 id : "alpine" ,
217196 title : "Alpine" ,
218- frameworkName : "Alpine" ,
219- frameworkNameId : "alpine" ,
220- isLatestStable : true ,
221197 img : "framework/alpine.svg" ,
222198 playgroundURL : "https://codesandbox.io/s/7br3q8" ,
223199 documentationURL : "https://alpinejs.dev/start-here" ,
@@ -231,9 +207,6 @@ export const frameworks: Framework[] = [
231207 {
232208 id : "mithril" ,
233209 title : "Mithril" ,
234- frameworkName : "Mithril" ,
235- frameworkNameId : "mithril" ,
236- isLatestStable : true ,
237210 img : "framework/mithril.svg" ,
238211 playgroundURL : "https://codesandbox.io/s/q99qzov66" ,
239212 documentationURL : "https://mithril.js.org/" ,
@@ -247,9 +220,6 @@ export const frameworks: Framework[] = [
247220 {
248221 id : "aurelia2" ,
249222 title : "Aurelia 2" ,
250- frameworkName : "Aurelia" ,
251- frameworkNameId : "aurelia" ,
252- isLatestStable : true ,
253223 img : "framework/aurelia.svg" ,
254224 playgroundURL : "https://stackblitz.com/edit/au2-conventions?file=src%2Fmy-app.html" ,
255225 documentationURL : "http://docs.aurelia.io" ,
@@ -263,9 +233,6 @@ export const frameworks: Framework[] = [
263233 {
264234 id : "qwik" ,
265235 title : "Qwik" ,
266- frameworkName : "Qwik" ,
267- frameworkNameId : "qwik" ,
268- isLatestStable : true ,
269236 img : "framework/qwik.svg" ,
270237 playgroundURL : "https://qwik.builder.io/playground" ,
271238 documentationURL : "https://qwik.builder.io/docs/overview" ,
@@ -279,9 +246,6 @@ export const frameworks: Framework[] = [
279246 {
280247 id : "marko" ,
281248 title : "Marko" ,
282- frameworkName : "Marko" ,
283- frameworkNameId : "marko" ,
284- isLatestStable : true ,
285249 img : "framework/marko.svg" ,
286250 playgroundURL : "https://markojs.com/playground/" ,
287251 documentationURL : "https://markojs.com/docs/getting-started/" ,
@@ -295,9 +259,6 @@ export const frameworks: Framework[] = [
295259 {
296260 id : "aurelia1" ,
297261 title : "Aurelia 1" ,
298- frameworkName : "Aurelia" ,
299- frameworkNameId : "aurelia" ,
300- isLatestStable : false ,
301262 img : "framework/aurelia.svg" ,
302263 playgroundURL : "https://codesandbox.io/s/ppmy26opw7" ,
303264 documentationURL : "http://aurelia.io/docs/" ,
@@ -311,9 +272,6 @@ export const frameworks: Framework[] = [
311272 {
312273 id : "ripple" ,
313274 title : "Ripple" ,
314- frameworkName : "Ripple" ,
315- frameworkNameId : "ripple" ,
316- isLatestStable : true ,
317275 img : "framework/ripple.svg" ,
318276 playgroundURL : "https://www.ripple-ts.com/playground" ,
319277 documentationURL : "https://www.ripple-ts.com/" ,
@@ -326,62 +284,80 @@ export const frameworks: Framework[] = [
326284 } ,
327285] ;
328286
329- export function matchFrameworkId ( id : string ) : Framework | undefined {
330- // First try to find by exact ID
331- let framework = frameworks . find ( ( f ) => f . id === id ) ;
287+ export const frameworks : Framework [ ] = [
288+ { id : "svelte" , name : "Svelte" , latestStable : "svelte5" } ,
289+ { id : "react" , name : "React" , latestStable : "react" } ,
290+ { id : "vue" , name : "Vue" , latestStable : "vue3" } ,
291+ { id : "angular" , name : "Angular" , latestStable : "angularRenaissance" } ,
292+ { id : "lit" , name : "Lit" , latestStable : "lit" } ,
293+ { id : "ember" , name : "Ember" , latestStable : "emberPolaris" } ,
294+ { id : "solid" , name : "Solid" , latestStable : "solid" } ,
295+ { id : "alpine" , name : "Alpine" , latestStable : "alpine" } ,
296+ { id : "mithril" , name : "Mithril" , latestStable : "mithril" } ,
297+ { id : "aurelia" , name : "Aurelia" , latestStable : "aurelia2" } ,
298+ { id : "qwik" , name : "Qwik" , latestStable : "qwik" } ,
299+ { id : "marko" , name : "Marko" , latestStable : "marko" } ,
300+ { id : "ripple" , name : "Ripple" , latestStable : "ripple" } ,
301+ ] ;
302+
303+ /**
304+ * Invariants, validated at module load so a misconfiguration fails fast instead
305+ * of silently resolving to the wrong version:
306+ * - version ids are unique,
307+ * - framework (family) ids are unique,
308+ * - each family's `latestStable` references an existing version.
309+ */
310+ function assertValidFrameworksConfig ( versions : FrameworkVersion [ ] , families : Framework [ ] ) : void {
311+ const errors : string [ ] = [ ] ;
312+
313+ const versionIds = new Set < string > ( ) ;
314+ for ( const { id } of versions ) {
315+ if ( versionIds . has ( id ) ) errors . push ( `duplicate version id "${ id } "` ) ;
316+ versionIds . add ( id ) ;
317+ }
332318
333- // If not found, try to find by framework name ID and return the latest stable version
334- if ( ! framework ) {
335- const latestStable = getLatestStableFrameworkByFrameworkName ( id ) ;
336- if ( latestStable ) {
337- framework = latestStable ;
319+ const familyIds = new Set < string > ( ) ;
320+ for ( const family of families ) {
321+ if ( familyIds . has ( family . id ) ) errors . push ( `duplicate framework id "${ family . id } "` ) ;
322+ familyIds . add ( family . id ) ;
323+ if ( ! versionIds . has ( family . latestStable ) ) {
324+ errors . push (
325+ `framework "${ family . id } ".latestStable points to unknown version "${ family . latestStable } "` ,
326+ ) ;
338327 }
339328 }
340329
341- return framework ;
330+ if ( errors . length > 0 ) {
331+ throw new Error ( `Invalid frameworks configuration:\n - ${ errors . join ( "\n - " ) } ` ) ;
332+ }
342333}
343334
344- /**
345- * Get all frameworks that belong to a specific framework name
346- */
347- export function getFrameworksByFrameworkName ( frameworkNameId : string ) : Framework [ ] {
348- return frameworks . filter ( ( framework ) => framework . frameworkNameId === frameworkNameId ) ;
349- }
335+ assertValidFrameworksConfig ( frameworkVersions , frameworks ) ;
350336
351337/**
352- * Get the latest stable framework for a given framework name
338+ * Resolve an id to a concrete framework version. Accepts either a framework
339+ * family id (e.g. "svelte" or "angular") or a version id (e.g. "svelte5").
340+ *
341+ * Family resolution takes precedence over version resolution: a generic name
342+ * always maps to its latest stable version, even when a (legacy) version shares
343+ * that id — e.g. "angular" resolves to Angular Renaissance, not the legacy
344+ * "angular" version.
353345 */
354- export function getLatestStableFrameworkByFrameworkName (
355- frameworkNameId : string ,
356- ) : Framework | undefined {
357- return frameworks . find (
358- ( framework ) => framework . frameworkNameId === frameworkNameId && framework . isLatestStable ,
359- ) ;
360- }
346+ export function matchFrameworkId ( id : string ) : FrameworkVersion | undefined {
347+ const family = frameworks . find ( ( f ) => f . id === id ) ;
348+ if ( family ) {
349+ const stable = frameworkVersions . find ( ( v ) => v . id === family . latestStable ) ;
350+ if ( stable ) return stable ;
351+ }
361352
362- /**
363- * Get all unique framework name IDs
364- */
365- export function getFrameworkNameIds ( ) : string [ ] {
366- return [ ...new Set ( frameworks . map ( ( framework ) => framework . frameworkNameId ) ) ] ;
353+ return frameworkVersions . find ( ( v ) => v . id === id ) ;
367354}
368355
369356/**
370- * Get framework name information including all versions and latest stable
357+ * Get the latest stable version of a framework family, e.g. "svelte" -> Svelte 5.
371358 */
372- export function getFrameworkNameInfo ( frameworkNameId : string ) : {
373- frameworkNameId : string ;
374- frameworks : Framework [ ] ;
375- latestStable : Framework | undefined ;
376- allVersions : string [ ] ;
377- } {
378- const familyFrameworks = getFrameworksByFrameworkName ( frameworkNameId ) ;
379- const latestStable = getLatestStableFrameworkByFrameworkName ( frameworkNameId ) ;
380-
381- return {
382- frameworkNameId,
383- frameworks : familyFrameworks ,
384- latestStable,
385- allVersions : familyFrameworks . map ( ( f ) => f . id ) ,
386- } ;
359+ export function getLatestStableVersion ( frameworkId : string ) : FrameworkVersion | undefined {
360+ const family = frameworks . find ( ( f ) => f . id === frameworkId ) ;
361+ if ( ! family ) return undefined ;
362+ return frameworkVersions . find ( ( v ) => v . id === family . latestStable ) ;
387363}
0 commit comments