Skip to content

Commit 7c0d9d9

Browse files
committed
Modification - Make getThemeConfig utility work with individual theme files #267
1 parent 573c872 commit 7c0d9d9

File tree

2 files changed

+112
-64
lines changed

2 files changed

+112
-64
lines changed

src/getThemeConfig.js

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,55 @@
1-
import themes from "./themes.json";
1+
const _globLoaders = import.meta.glob('./themes/*.json', { import: 'default' });
22

3-
export default function getThemeConfig(type) {
4-
return themes[type]
5-
}
3+
const THEME_LOADERS = Object.fromEntries(
4+
Object.entries(_globLoaders).map(([path, load]) => {
5+
const m = path.match(/\/themes\/(.+)\.json$/);
6+
const type = m ? m[1] : path;
7+
return [type, load];
8+
})
9+
);
10+
11+
const _cache = new Map();
12+
13+
let _loader = async (type) => {
14+
const load = THEME_LOADERS[type];
15+
if (!load) {
16+
const err = new Error('ENOENT');
17+
err.code = 'ENOENT';
18+
throw err;
19+
}
20+
return await load();
21+
};
22+
23+
async function getThemeConfig(type) {
24+
if (!type) return null;
25+
if (_cache.has(type)) return _cache.get(type);
26+
try {
27+
const theme = await _loader(type);
28+
_cache.set(type, theme);
29+
return theme;
30+
} catch (e) {
31+
console.warn(`[getThemeConfig] Missing theme file: ${type}.json`, e);
32+
return null;
33+
}
34+
}
35+
36+
export default getThemeConfig;
37+
export { getThemeConfig };
38+
39+
export function getThemeConfigSync(type) {
40+
if (!type) return null;
41+
return _cache.get(type) ?? null;
42+
}
43+
44+
export async function preloadTheme(type) {
45+
return await getThemeConfig(type);
46+
}
47+
export async function preloadThemes(types = []) {
48+
return Promise.all(types.map(getThemeConfig));
49+
}
50+
51+
export const __testing = {
52+
clearCache: () => _cache.clear(),
53+
cacheSize: () => _cache.size,
54+
setLoader: (fn) => { _loader = fn; },
55+
};

tests/getThemeConfig.test.js

Lines changed: 58 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,60 @@
1-
import { expect, test, describe } from "vitest";
2-
import sut from '../src/themes.json'
3-
import getThemeConfig from "../src/getThemeConfig";
4-
5-
const components = [
6-
'3d_bar',
7-
'age_pyramid',
8-
'candlestick',
9-
'chestnut',
10-
'donut',
11-
'donut_evolution',
12-
'dumbbell',
13-
'galaxy',
14-
'gauge',
15-
'heatmap',
16-
'molecule',
17-
'mood_radar',
18-
'nested_donuts',
19-
'onion',
20-
'parallel_coordinate_plot',
21-
'quadrant',
22-
'quick_chart',
23-
'radar',
24-
'relation_circle',
25-
'rings',
26-
'scatter',
27-
'spark_trend',
28-
'sparkbar',
29-
'sparkgauge',
30-
'sparkhistogram',
31-
'sparkline',
32-
'sparkstackbar',
33-
'strip_plot',
34-
'table_heatmap',
35-
'table_sparkline',
36-
'thermometer',
37-
'tiremarks',
38-
'treemap',
39-
'vertical_bar',
40-
'waffle',
41-
'wheel',
42-
'xy',
43-
'xy_canvas'
44-
]
45-
46-
const themes = [
47-
"default",
48-
"zen",
49-
"hack",
50-
"concrete"
51-
]
1+
import { describe, test, expect, vi, beforeEach } from 'vitest';
2+
import { preloadTheme, getThemeConfigSync, __testing } from '../src/getThemeConfig';
523

534
describe('getThemeConfig', () => {
54-
components.forEach(component => {
55-
themes.forEach(theme => {
56-
test(`returns vue_ui_${component} ${theme} theme`, () => {
57-
expect(getThemeConfig(`vue_ui_${component}`)[theme]).not.toBeUndefined()
58-
expect(getThemeConfig(`vue_ui_${component}`)[theme]).toStrictEqual(sut[`vue_ui_${component}`][theme])
59-
})
60-
})
61-
})
62-
})
5+
let calls;
6+
let loader;
7+
8+
beforeEach(() => {
9+
__testing.clearCache();
10+
calls = 0;
11+
12+
// Fake loader so tests don't touch the real filesystem
13+
loader = vi.fn(async (type) => {
14+
calls += 1;
15+
if (type === 'vue_ui_chord') {
16+
return { debug: true };
17+
}
18+
const err = new Error('ENOENT');
19+
err.code = 'ENOENT';
20+
throw err;
21+
});
22+
23+
__testing.setLoader(loader);
24+
});
25+
26+
test('returns theme object when JSON file exists (after preload)', async () => {
27+
await preloadTheme('vue_ui_chord'); // loads once
28+
const theme = getThemeConfigSync('vue_ui_chord'); // sync read
29+
expect(theme).toEqual({ debug: true });
30+
expect(calls).toBe(1);
31+
});
32+
33+
test('returns null when theme file does not exist', async () => {
34+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
35+
const res = await preloadTheme('non_existing_theme');
36+
expect(res).toBeNull();
37+
expect(warnSpy).toHaveBeenCalled();
38+
expect(calls).toBe(1);
39+
const theme = getThemeConfigSync('non_existing_theme');
40+
expect(theme).toBeNull();
41+
warnSpy.mockRestore();
42+
});
43+
44+
test('uses cached theme on second call (module loaded once)', async () => {
45+
await preloadTheme('vue_ui_chord'); // first (and only) load
46+
const first = getThemeConfigSync('vue_ui_chord'); // from cache
47+
const second = getThemeConfigSync('vue_ui_chord'); // from cache
48+
expect(second).toBe(first); // same ref
49+
expect(calls).toBe(1); // loader once
50+
expect(__testing.cacheSize()).toBe(1);
51+
});
52+
53+
test('returns null when called with undefined or empty type', () => {
54+
const a = getThemeConfigSync();
55+
const b = getThemeConfigSync('');
56+
expect(a).toBeNull();
57+
expect(b).toBeNull();
58+
expect(calls).toBe(0);
59+
});
60+
});

0 commit comments

Comments
 (0)