Skip to content

Commit

Permalink
Merge pull request #1 from leynier/next
Browse files Browse the repository at this point in the history
feat: ensure wraps only promise-returning functions and handles non-promise returns
  • Loading branch information
leynier authored Nov 23, 2024
2 parents 5742a47 + 6c1489b commit 138cd1d
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 17 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ pnpm-debug.log*

# Idea/VSCode settings
.idea/
.vscode/
.vscode/*
!.vscode/settings.json

# Miscellaneous
*.tgz
10 changes: 10 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"singleQuote": true,
"vueIndentScriptAndStyle": true,
"singleAttributePerLine": true,
"htmlWhitespaceSensitivity": "strict",
"arrowParens": "avoid",
"bracketSameLine": true,
"jsxSingleQuote": true,
"proseWrap": "always"
}
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nuxt-use-async-data-wrapper",
"version": "1.0.1",
"version": "1.1.0",
"description": "A utility to wrap Promise-returning functions with useAsyncData for Nuxt",
"main": "lib/index.js",
"types": "lib/index.d.ts",
Expand All @@ -25,10 +25,11 @@
"url": "https://github.com/leynier/nuxt-use-async-data-wrapper.git"
},
"peerDependencies": {
"vue": "^3.0.0",
"nuxt": "^3.0.0"
"nuxt": "^3.0.0",
"vue": "^3.0.0"
},
"devDependencies": {
"prettier": "^3.3.3",
"typescript": "^5.7.2",
"vue": "^3.5.13"
},
Expand Down
10 changes: 10 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 34 additions & 13 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@ type AsyncDataResult<T> = AsyncData<T, Error>;
/**
* Transforms an object's Promise-returning functions into functions compatible with useAsyncData.
*
* Only includes functions that return Promises; other properties are excluded.
*
* For each function in the object:
* - If the function returns a Promise and takes no arguments, it becomes a function that accepts optional AsyncDataOptions.
* - If the function returns a Promise and takes arguments, it becomes a function that accepts an argsSupplier and optional AsyncDataOptions.
*
* This allows you to use the functions within a Nuxt application, leveraging the reactivity and data fetching capabilities of useAsyncData.
*
* @template T - The type of the object.
*/
export type AsyncDataWrapper<T> = {
[K in keyof T]: T[K] extends (...args: infer Args) => Promise<infer R>
[K in keyof T as T[K] extends (...args: any[]) => Promise<any>
? K
: never]: T[K] extends (...args: infer Args) => Promise<infer R>
? Args extends []
? /**
* Functions without arguments.
Expand All @@ -33,7 +35,10 @@ export type AsyncDataWrapper<T> = {
* @param options - Optional AsyncDataOptions to configure useAsyncData.
* @returns AsyncDataResult containing the data, pending state, and error.
*/
(argsSupplier: () => Args, options?: AsyncDataOptions<R>) => AsyncDataResult<R>
(
argsSupplier: () => Args,
options?: AsyncDataOptions<R>,
) => AsyncDataResult<R>
: never;
};

Expand All @@ -56,13 +61,15 @@ export type AsyncDataWrapper<T> = {
* const wrappedObject = useAsyncDataWrapper(originalObject);
* ```
*/
export function useAsyncDataWrapper<T extends Record<string, any>>(obj: T): AsyncDataWrapper<T> {
export function useAsyncDataWrapper<T extends Record<string, any>>(
obj: T,
): AsyncDataWrapper<T> {
const composable = {} as AsyncDataWrapper<T>;
const proto = Object.getPrototypeOf(obj);

// Get function names from the object's prototype, excluding the constructor
const functionNames = Object.getOwnPropertyNames(proto).filter(
key => key !== 'constructor' && typeof obj[key] === 'function'
key => key !== 'constructor' && typeof obj[key] === 'function',
);

for (const key of functionNames) {
Expand All @@ -89,27 +96,41 @@ export function useAsyncDataWrapper<T extends Record<string, any>>(obj: T): Asyn
// Reactive reference to arguments
const argsRef = computed(() => argsSupplier!());
// Unique key for useAsyncData
const dataKeyRef = computed(() => `${key}-${JSON.stringify(argsRef.value)}`);
const dataKeyRef = computed(
() => `${key}-${JSON.stringify(argsRef.value)}`,
);

// Call useAsyncData with the generated key and function
const asyncDataResult = useAsyncData(
dataKeyRef.value,
() => originalFunction(...unref(argsRef)),
() => {
const result = originalFunction(...unref(argsRef));
// Ensure we return a Promise
return result instanceof Promise ? result : Promise.resolve(result);
},
{
// Re-execute when arguments change
watch: [argsRef],
// Spread additional options
...options,
}
},
);

return asyncDataResult;
} else {
// For functions without arguments
const asyncDataResult = useAsyncData(key, () => originalFunction(), {
// Spread additional options
...options,
});
const asyncDataResult = useAsyncData(
key,
() => {
const result = originalFunction();
// Ensure we return a Promise
return result instanceof Promise ? result : Promise.resolve(result);
},
{
// Spread additional options
...options,
},
);

return asyncDataResult;
}
Expand Down

0 comments on commit 138cd1d

Please sign in to comment.