@@ -45,13 +45,24 @@ interface StylesIndexCacheEntry {
4545 styleNames : readonly ( string | undefined ) [ ] ;
4646}
4747
48+ interface StylesMapIndexCacheEntry {
49+ index : StylesIndex ;
50+ keys : readonly string [ ] ;
51+ styleRefs : readonly ( StyleDefinition | undefined ) [ ] ;
52+ styleIds : readonly ( string | undefined ) [ ] ;
53+ styleNames : readonly ( string | undefined ) [ ] ;
54+ }
55+
56+ type LegacyStylesMap = Record < string , StyleDefinition > ;
57+
4858/**
4959 * Module-level cache keyed by style-array reference.
5060 *
5161 * The cache also tracks per-item identity and key fields so in-place array
5262 * mutations (push/splice/reorder/styleId renames) invalidate safely.
5363 */
5464const indexCache = new WeakMap < readonly StyleDefinition [ ] , StylesIndexCacheEntry > ( ) ;
65+ const mapIndexCache = new WeakMap < LegacyStylesMap , StylesMapIndexCacheEntry > ( ) ;
5566const EMPTY_STYLES : readonly StyleDefinition [ ] = [ ] ;
5667
5768function createCacheEntry ( styles : readonly StyleDefinition [ ] ) : StylesIndexCacheEntry {
@@ -78,23 +89,80 @@ function isCacheEntryValid(styles: readonly StyleDefinition[], entry: StylesInde
7889 return true ;
7990}
8091
92+ function isStyleMap ( styles : unknown ) : styles is LegacyStylesMap {
93+ return typeof styles === 'object' && styles !== null && ! Array . isArray ( styles ) ;
94+ }
95+
96+ function createMapCacheEntry ( stylesMap : LegacyStylesMap ) : StylesMapIndexCacheEntry {
97+ const keys = Object . keys ( stylesMap ) ;
98+ const normalized : StyleDefinition [ ] = [ ] ;
99+ const styleRefs : Array < StyleDefinition | undefined > = [ ] ;
100+ const styleIds : Array < string | undefined > = [ ] ;
101+ const styleNames : Array < string | undefined > = [ ] ;
102+
103+ for ( const key of keys ) {
104+ const style = stylesMap [ key ] ;
105+ styleRefs . push ( style ) ;
106+ styleIds . push ( style ?. styleId ) ;
107+ styleNames . push ( style ?. name ) ;
108+ if ( ! style ) continue ;
109+ normalized . push ( style . styleId ? style : { ...style , styleId : key } ) ;
110+ }
111+
112+ return {
113+ index : new StylesIndex ( normalized ) ,
114+ keys,
115+ styleRefs,
116+ styleIds,
117+ styleNames,
118+ } ;
119+ }
120+
121+ function isMapCacheEntryValid ( stylesMap : LegacyStylesMap , entry : StylesMapIndexCacheEntry ) : boolean {
122+ const keys = Object . keys ( stylesMap ) ;
123+ if ( keys . length !== entry . keys . length ) {
124+ return false ;
125+ }
126+
127+ for ( let i = 0 ; i < keys . length ; i += 1 ) {
128+ const key = keys [ i ] ;
129+ if ( key !== entry . keys [ i ] ) return false ;
130+
131+ const style = stylesMap [ key ] ;
132+ if ( style !== entry . styleRefs [ i ] ) return false ;
133+ if ( style ?. styleId !== entry . styleIds [ i ] ) return false ;
134+ if ( style ?. name !== entry . styleNames [ i ] ) return false ;
135+ }
136+
137+ return true ;
138+ }
139+
81140function getStylesIndex ( styles : unknown ) : StylesIndex {
82- // Invariant: normal editor flows (DOCX import and collaboration re-hydration)
83- // normalize style definitions to an ordered array before style resolution.
84- // Non-array input indicates an out-of-contract caller, so treat it as
85- // "no styles" instead of attempting legacy map compatibility here.
86- if ( ! Array . isArray ( styles ) ) {
87- return new StylesIndex ( EMPTY_STYLES ) ;
141+ // Primary format is an ordered array, but we still accept legacy map-shaped
142+ // catalogs keyed by styleId for backward compatibility.
143+ if ( Array . isArray ( styles ) ) {
144+ const cachedEntry = indexCache . get ( styles ) ;
145+ if ( cachedEntry && isCacheEntryValid ( styles , cachedEntry ) ) {
146+ return cachedEntry . index ;
147+ }
148+
149+ const entry = createCacheEntry ( styles ) ;
150+ indexCache . set ( styles , entry ) ;
151+ return entry . index ;
88152 }
89153
90- const cachedEntry = indexCache . get ( styles ) ;
91- if ( cachedEntry && isCacheEntryValid ( styles , cachedEntry ) ) {
92- return cachedEntry . index ;
154+ if ( isStyleMap ( styles ) ) {
155+ const cachedMapEntry = mapIndexCache . get ( styles ) ;
156+ if ( cachedMapEntry && isMapCacheEntryValid ( styles , cachedMapEntry ) ) {
157+ return cachedMapEntry . index ;
158+ }
159+
160+ const entry = createMapCacheEntry ( styles ) ;
161+ mapIndexCache . set ( styles , entry ) ;
162+ return entry . index ;
93163 }
94164
95- const entry = createCacheEntry ( styles ) ;
96- indexCache . set ( styles , entry ) ;
97- return entry . index ;
165+ return new StylesIndex ( EMPTY_STYLES ) ;
98166}
99167
100168export interface OoxmlResolverParams {
0 commit comments