From 371e42e4ee423a5c44db11ba32c7184454d81e74 Mon Sep 17 00:00:00 2001 From: Samuel Andert Date: Wed, 26 Jul 2023 18:28:31 +0200 Subject: [PATCH] Added dynamic dataLoader importing and fixing race conditions --- src/lib/core/Composite.svelte | 66 ++++++++++++++--------- src/lib/core/dataLoader.ts | 33 +++++++++--- src/lib/data/queryMessages.ts | 2 +- src/lib/data/queryTodos.ts | 2 +- src/lib/services/messages/messages.ts | 75 ++------------------------- vite.config.ts | 40 ++++++++++++++ 6 files changed, 112 insertions(+), 106 deletions(-) diff --git a/src/lib/core/Composite.svelte b/src/lib/core/Composite.svelte index 872aef1..f65114a 100644 --- a/src/lib/core/Composite.svelte +++ b/src/lib/core/Composite.svelte @@ -26,33 +26,43 @@ } export let composite: IComposite; - let loadedServices: Record = {}; + let layoutStyle = ''; - // Reactive loading mechanism for services based on composite changes - $: if (composite?.services) { - composite.services.forEach(async (serviceName) => { - if (!loadedServices[serviceName]) { - loadedServices[serviceName] = await loadService(serviceName); + $: { + // Create layout style reactively + layoutStyle = computeLayoutStyle(composite?.layout); + + // Load services reactively + if (composite?.services) { + for (const serviceName of composite.services) { + if (!loadedServices[serviceName]) { + // Note: We're ignoring async operation here, you might want to handle it if needed + loadService(serviceName).then((service) => { + loadedServices[serviceName] = service; + }); + } } - }); + } + + // Initialize composite state reactively + if (composite?.children) { + composite.children.forEach((child) => { + initializeCompositeState(child); + mapAndSubscribe(child); + }); + } } - $: layoutStyle = composite?.layout - ? ` - grid-template-areas: ${composite.layout.areas}; - ${composite.layout.gap ? `gap: ${composite.layout.gap};` : ''} - ${composite.layout.columns ? `grid-template-columns: ${composite.layout.columns};` : ''} - ${composite.layout.rows ? `grid-template-rows: ${composite.layout.rows};` : ''} - ` - : ''; + function computeLayoutStyle(layout?: ICompositeLayout) { + if (!layout) return ''; - // Reactive statement to watch changes in the composite prop - $: if (composite?.children) { - composite.children.forEach((child) => { - initializeCompositeState(child); - mapAndSubscribe(child); - }); + return ` + grid-template-areas: ${layout.areas}; + ${layout.gap ? `gap: ${layout.gap};` : ''} + ${layout.columns ? `grid-template-columns: ${layout.columns};` : ''} + ${layout.rows ? `grid-template-rows: ${layout.rows};` : ''} + `; } function initializeCompositeState(child: IComposite) { @@ -65,6 +75,8 @@ } } + let unsubscribers = []; + function mapAndSubscribe(component: IComposite) { console.log('Mapping and subscribing for:', component.id); @@ -77,14 +89,13 @@ if (externalID === 'data') { const unsubscribe = dataStore.subscribe((store) => { if (externalKey in store) { - // Check if the data item is a Svelte store if (store[externalKey] && typeof store[externalKey].subscribe === 'function') { let innerUnsub = store[externalKey].subscribe((value) => { localStore.update((storeValue) => { return { ...storeValue, [localKey]: value }; }); }); - onDestroy(innerUnsub); + unsubscribers.push(innerUnsub); } else { localStore.update((storeValue) => { return { ...storeValue, [localKey]: store[externalKey] }; @@ -93,7 +104,7 @@ } }); - onDestroy(unsubscribe); + unsubscribers.push(unsubscribe); } else { const externalStore = getCompositeStore(externalID); if (externalStore) { @@ -105,7 +116,7 @@ } }); - onDestroy(unsubscribe); + unsubscribers.push(unsubscribe); } } } @@ -116,6 +127,11 @@ } } + // Call all unsubscribe methods when the component is destroyed. + onDestroy(() => { + unsubscribers.forEach((unsub) => unsub()); + }); + async function getComponent(componentName: string) { if (components[componentName]) { const module = await components[componentName](); diff --git a/src/lib/core/dataLoader.ts b/src/lib/core/dataLoader.ts index 8884389..1509172 100644 --- a/src/lib/core/dataLoader.ts +++ b/src/lib/core/dataLoader.ts @@ -1,11 +1,30 @@ // dataLoader.ts - import { writable } from 'svelte/store'; -import { queryMessagesData } from '$lib/data/queryMessages'; -import { queryTodosData } from '$lib/data/queryTodos'; +import dataSources from 'virtual:data-sources-list'; // Import the generated list // The store that holds the data sets -export const dataStore = writable({ - queryMessages: queryMessagesData, - queryTodos: queryTodosData -}); \ No newline at end of file +export const dataStore = writable({}); + +// Dynamically import the data modules and assign them to the store +dataSources.forEach(src => { + import(`/src/lib/data/${src}.ts`).then(module => { + // Here, explicitly extract the required data or function from the module + const moduleData = module[src] || module.default; + + dataStore.update(store => ({ ...store, [src]: moduleData })); + }); +}); + + + +// // dataLoader.ts + +// import { writable } from 'svelte/store'; +// import { queryMessages } from '$lib/data/queryMessages'; +// import { queryTodos } from '$lib/data/queryTodos'; + +// // The store that holds the data sets +// export const dataStore = writable({ +// queryMessages: queryMessages, +// queryTodos: queryTodos +// }); \ No newline at end of file diff --git a/src/lib/data/queryMessages.ts b/src/lib/data/queryMessages.ts index 513edbc..ac69284 100644 --- a/src/lib/data/queryMessages.ts +++ b/src/lib/data/queryMessages.ts @@ -35,4 +35,4 @@ messages.subscribe(currentMessages => { setToLocalStorage('chat-messages', currentMessages); }); -export const queryMessagesData = messages; +export const queryMessages = messages; diff --git a/src/lib/data/queryTodos.ts b/src/lib/data/queryTodos.ts index c77ef70..f3539d9 100644 --- a/src/lib/data/queryTodos.ts +++ b/src/lib/data/queryTodos.ts @@ -1,7 +1,7 @@ // $lib/data/queryTodos.ts import { writable } from 'svelte/store'; -export const queryTodosData = writable([ +export const queryTodos = writable([ { id: "id1", text: "todo 1" diff --git a/src/lib/services/messages/messages.ts b/src/lib/services/messages/messages.ts index a3a40f1..2242821 100644 --- a/src/lib/services/messages/messages.ts +++ b/src/lib/services/messages/messages.ts @@ -1,6 +1,6 @@ // $lib/services/messages.ts -import { queryMessagesData } from '$lib/data/queryMessages'; +import { queryMessages } from '$lib/data/queryMessages'; // The createMessage function now accepts a messageData parameter. export function createMessage(messageData) { @@ -15,78 +15,9 @@ export function createMessage(messageData) { // We use the $ prefix to get the value out of a Svelte store // and then set the new value back into the store. - queryMessagesData.update(oldMessages => [...oldMessages, newMessageObj]); + queryMessages.update(oldMessages => [...oldMessages, newMessageObj]); } export function clearMessages() { - queryMessagesData.set([]); + queryMessages.set([]); } - - -// import { writable } from 'svelte/store'; - -// // Helper function to determine if we're running on the client side (browser) or server side. -// function isClientSide() { -// return typeof window !== "undefined"; -// } - -// // Safely get item from localStorage -// function getFromLocalStorage(key, defaultValue) { -// if (isClientSide()) { -// return localStorage.getItem(key) -// ? JSON.parse(localStorage.getItem(key)) -// : defaultValue; -// } -// return defaultValue; -// } - -// // Safely set item to localStorage -// function setToLocalStorage(key, value) { -// if (isClientSide()) { -// localStorage.setItem(key, JSON.stringify(value)); -// } -// } - -// // Define the updated Message interface -// export interface Message { -// text: string; -// timestamp: string; -// sender: string; -// type: string; -// composite?: object | null; // New field -// } - - -// // Load messages from localStorage or set an empty array if not available -// const initialMessages = getFromLocalStorage('chat-messages', []); - -// // Convert the array to a writable store -// export const messages = writable(initialMessages); - -// // Subscribe to messages store to watch for changes and save them to localStorage -// messages.subscribe(currentMessages => { -// setToLocalStorage('chat-messages', currentMessages); -// }); - - -// export function createMessage(messageData) { -// const currentDate = new Date().toLocaleString(); -// const newMessageObj = { -// text: messageData.text, -// timestamp: currentDate, -// sender: messageData.sender, -// type: messageData.type, -// composite: messageData.composite || null // New field -// }; -// messages.update(oldMessages => [...oldMessages, newMessageObj]); -// } -// export function clearMessages() { -// messages.set([]); -// } - -// // Dummy messages -// export const messagesList: Message[] = [ -// { text: "Hello there!", timestamp: new Date().toLocaleString(), sender: "John", type: "text" }, -// { text: "How are you?", timestamp: new Date().toLocaleString(), sender: "Alice", type: "text" }, -// // Add more dummy messages here with the timestamp and sender properties -// ]; diff --git a/vite.config.ts b/vite.config.ts index 326f53b..5cdce61 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -34,6 +34,22 @@ function getRecursiveServiceFiles(dir) { return files; } +function getRecursiveDataFiles(dir) { + const dirents = fs.readdirSync(dir, { withFileTypes: true }); + const files = Array.from(dirents).flatMap((dirent) => { + const res = resolve(dir, dirent.name); + if (dirent.isDirectory()) { + return getRecursiveDataFiles(res); + } else if (res.endsWith('.ts')) { + return [res]; + } else { + return []; + } + }); + return files; +} + + export default defineConfig({ plugins: [ sveltekit(), @@ -84,6 +100,30 @@ export default defineConfig({ } return null; } + }, + { + name: 'data-sources-resolver', + resolveId(source) { + if (source === 'virtual:data-sources-list') return source; + return null; + }, + load(id) { + if (id === 'virtual:data-sources-list') { + const dataDir = resolve(__dirname, 'src/lib/data'); + const dataFiles = getRecursiveServiceFiles(dataDir); // Use the same function as before + + const dataSources = dataFiles.map(file => + file + .replace(dataDir, '') + .replace(/\.ts$/, '') + .replace(/\\/g, '/') // Fix Windows path separators + .slice(1) // Remove leading "/" + ); + + return `export default ${JSON.stringify(dataSources)};`; + } + return null; + } } ], test: {