Skip to content

Commit 113072b

Browse files
committed
chore: fix tests
1 parent eb3e11f commit 113072b

3 files changed

Lines changed: 117 additions & 18 deletions

File tree

packages/layout-engine/style-engine/src/ooxml/index.ts

Lines changed: 80 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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
*/
5464
const indexCache = new WeakMap<readonly StyleDefinition[], StylesIndexCacheEntry>();
65+
const mapIndexCache = new WeakMap<LegacyStylesMap, StylesMapIndexCacheEntry>();
5566
const EMPTY_STYLES: readonly StyleDefinition[] = [];
5667

5768
function 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+
81140
function 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

100168
export interface OoxmlResolverParams {

packages/layout-engine/style-engine/src/ooxml/table-style-selection.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ import {
99
} from './table-style-selection.ts';
1010
import type { StylesDocumentProperties } from './styles-types.ts';
1111

12-
const emptyStyles: StylesDocumentProperties = { docDefaults: {}, latentStyles: {}, styles: {} };
12+
const emptyStyles = { docDefaults: {}, latentStyles: {}, styles: {} } as unknown as StylesDocumentProperties;
1313

1414
const withStyles = (styles: Record<string, { type?: string; default?: boolean }>): StylesDocumentProperties => ({
1515
...emptyStyles,
16-
styles: Object.fromEntries(Object.entries(styles).map(([id, def]) => [id, { styleId: id, ...def }])),
16+
styles: Object.fromEntries(
17+
Object.entries(styles).map(([id, def]) => [id, { styleId: id, ...def }]),
18+
) as unknown as StylesDocumentProperties['styles'],
1719
});
1820

1921
// ──────────────────────────────────────────────────────────────────────────────

packages/layout-engine/style-engine/src/ooxml/table-style-selection.ts

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,23 @@ export interface ResolvedStyle {
4141
source: ResolvedStyleSource;
4242
}
4343

44+
function lookupStyleById(
45+
styles: StylesDocumentProperties['styles'] | null | undefined,
46+
styleId: string,
47+
): StyleDefinition | undefined {
48+
if (!styles) return undefined;
49+
50+
if (Array.isArray(styles)) {
51+
return styles.find((candidate) => candidate.styleId === styleId);
52+
}
53+
54+
const legacyMap = styles as unknown as Record<string, StyleDefinition>;
55+
const direct = legacyMap[styleId];
56+
if (direct) return direct;
57+
58+
return Object.values(legacyMap).find((candidate) => candidate?.styleId === styleId);
59+
}
60+
4461
// ──────────────────────────────────────────────────────────────────────────────
4562
// Helpers
4663
// ──────────────────────────────────────────────────────────────────────────────
@@ -53,7 +70,7 @@ export function isKnownTableStyleId(
5370
translatedLinkedStyles: StylesDocumentProperties | null | undefined,
5471
): boolean {
5572
if (!styleId || !translatedLinkedStyles?.styles) return false;
56-
const def = translatedLinkedStyles.styles[styleId];
73+
const def = lookupStyleById(translatedLinkedStyles.styles, styleId);
5774
return def != null && def.type === 'table';
5875
}
5976

@@ -65,11 +82,23 @@ export function findTypeDefaultTableStyleId(
6582
translatedLinkedStyles: StylesDocumentProperties | null | undefined,
6683
): string | null {
6784
if (!translatedLinkedStyles?.styles) return null;
68-
for (const [styleId, def] of Object.entries(translatedLinkedStyles.styles)) {
69-
if (def.type === 'table' && def.default === true) {
70-
return styleId;
85+
86+
if (Array.isArray(translatedLinkedStyles.styles)) {
87+
for (const def of translatedLinkedStyles.styles) {
88+
if (def.type === 'table' && def.default === true && def.styleId) {
89+
return def.styleId;
90+
}
7191
}
92+
return null;
7293
}
94+
95+
const legacyMap = translatedLinkedStyles.styles as unknown as Record<string, StyleDefinition>;
96+
for (const [key, def] of Object.entries(legacyMap)) {
97+
if (def?.type === 'table' && def.default === true) {
98+
return def.styleId ?? key;
99+
}
100+
}
101+
73102
return null;
74103
}
75104

0 commit comments

Comments
 (0)