Skip to content

Commit

Permalink
Merge pull request #222 from mainmatter/transform-function
Browse files Browse the repository at this point in the history
feat: add `transform` function to annotate a function to be transformed by the vite plugin
  • Loading branch information
paoloricciuti authored Nov 8, 2024
2 parents 7cdb99f + f087d0b commit 69f253d
Show file tree
Hide file tree
Showing 24 changed files with 1,616 additions and 92 deletions.
4 changes: 4 additions & 0 deletions apps/docs/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ export default defineConfig({
label: 'Task Instance',
link: '/reference/task-instance',
},
{
label: 'Transform',
link: '/reference/transform',
},
],
},
],
Expand Down
4 changes: 2 additions & 2 deletions apps/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"@astrojs/check": "^0.9.0",
"@astrojs/netlify": "^5.4.0",
"@astrojs/starlight": "^0.28.0",
"@astrojs/svelte": "^5.4.0",
"@astrojs/svelte": "^5.7.2",
"@fontsource/calistoga": "^5.0.21",
"@fontsource/pt-sans": "^5.0.13",
"@sheepdog/svelte": "workspace:*",
Expand All @@ -27,4 +27,4 @@
"volta": {
"extends": "../../package.json"
}
}
}
96 changes: 95 additions & 1 deletion apps/docs/src/content/docs/explainers/async-transform.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ confident in using it. If you want to know how to setup the Vite plugin please g
link.

<LinkCard
href="getting-started/installation/#setup-the-async-transform"
href="/getting-started/installation/#setup-the-async-transform"
title="Setup the Async Transform"
description="Read the short guide about how to setup the Async Transform"
/>
Expand Down Expand Up @@ -264,3 +264,97 @@ and the same works even for imported functions with the same name.
</TabItem>

</Tabs>

However sometimes you might want to have a single function to create multiple tasks or maybe you
want to have your tasks in a separate ts file. Well good news for you...

## The `transform` function

If you want to tell `@sheepdog/svelte` that a function is meant to be transformed because you will
pass that function to a task you can do so by using the `transform` function exported from
`@sheepdog/svelte/utils`.

This function doesn't do anything at runtime but it serves to purpose:

1. As a marker for the vite plugin to transform the function you pass to it.
2. as a "safety measure". The function will actually throw in `DEV` if you use it without the Async
Transform. This will warn you that what you expect to be a mid-run-cancellable function it is not
because you forgot to add the vite plugin.

Here's how a transformed function will look like

<Tabs>

<TabItem label="Before">

```ts
import { transform } from '@sheepdog/svelte/utils';

// this will be converted
const myTask = transform(async () => {
const result = await fetch('/api/my-endpoint');
return await result.json();
});
```

</TabItem>

<TabItem label="After">

```ts
// this will be converted
const myTask = async function* () {
const result = yield fetch('/api/my-endpoint');
return yield result.json();
};
```

</TabItem>

</Tabs>

as you can see the function invocation was completely removed. This obviously works the same if you
rename your import.

<Tabs>

<TabItem label="Before">

```ts
import { transform as sheepdogTransform } from '@sheepdog/svelte/utils';
import { transform } from 'random-npm-library';

// this will remain untouched
const otherTransform = transform(async () => {
// async stuff
});

// this will be converted
const myTask = sheepdogTransform(async () => {
const result = await fetch('/api/my-endpoint');
return await result.json();
});
```

</TabItem>

<TabItem label="After">

```ts
import { transform } from 'random-npm-library';

// this will remain untouched
const otherTransform = transform(async () => {
// async stuff
});

// this will be converted
const myTask = async function* () {
const result = yield fetch('/api/my-endpoint');
return yield result.json();
};
```

</TabItem>

</Tabs>
3 changes: 3 additions & 0 deletions apps/docs/src/content/docs/getting-started/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,6 @@ export default defineConfig(({ mode }) => ({
```

And that's it! You can now use async tasks with proper mid-run cancellation!

Remember: the Async Transform will only change your code in some very specific places, read more
about it in [the async transform explainer](/explainers/async-transform).
43 changes: 43 additions & 0 deletions apps/docs/src/content/docs/reference/transform.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
title: Transform
description: the transform function
---

import { LinkCard, Tabs, TabItem, Aside } from '@astrojs/starlight/components';

This function allows you to mark a function that is not directly passed to the `task` function as
transformable from the Async Transform.

<LinkCard
href="/explainers/async-transform"
title="Async Transform"
description="Read more about the Async Transform"
/>

## Usage

You can import this function from `@sheepdog/svelte/utils` and call it with the same first argument
you would pass to the `task` function

```ts
import { transform } from '@sheepdog/svelte/utils';

const myTask = transform(async (id: number) => {
const res = await fetch(`/products/${id}`);
return await res.json();
});
```

and this is really all you need to do. If you added the Async Transform vite plugin this function
will be transformed and the `transform` import will be removed

```ts
const myTask = async function* (id: number) {
const res = yield fetch(`/products/${id}`);
return yield res.json();
};
```

If by any chance you forgot to add the vite plugin the function will just return the function as is
but it will throw an error at runtime (only in dev mode) to let you know that you need to include
the vite plugin to actually transform the function.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
"pnpm": {
"patchedDependencies": {
"fixturify-project": "patches/fixturify-project.patch"
},
"overrides": {
"@sveltejs/vite-plugin-svelte": "^4.0.0"
}
},
"packageManager": "[email protected]+sha512.cce0f9de9c5a7c95bef944169cc5dfe8741abfb145078c0d508b868056848a87c81e626246cb60967cbd7fd29a6c062ef73ff840d96b3c86c40ac92cf4a813ee"
Expand Down
3 changes: 2 additions & 1 deletion packages/svelte/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ node_modules
!.env.example
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
coverage
coverage
src/lib/tests/expected-transforms/**/_actual.js
1 change: 1 addition & 0 deletions packages/svelte/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
},
"dependencies": {
"acorn": "^8.11.3",
"esm-env": "^1.1.4",
"esrap": "^1.2.2",
"zimmerframe": "^1.1.2"
},
Expand Down
10 changes: 6 additions & 4 deletions packages/svelte/src/lib/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,14 @@ export class CancelationError extends Error {
}
}

export type TaskFunction<TArgs = unknown, TReturn = unknown> = (
args: TArgs,
utils: SheepdogUtils,
) => Promise<TReturn> | AsyncGenerator<unknown, TReturn, unknown>;

export function createTask<TArgs = unknown, TReturn = unknown, TModifier = object>(
adapter: TaskAdapter<TReturn, TModifier>,
gen_or_fun: (
args: TArgs,
utils: SheepdogUtils,
) => Promise<TReturn> | AsyncGenerator<unknown, TReturn, unknown>,
gen_or_fun: TaskFunction<TArgs, TReturn>,
options?: TaskOptions,
) {
const handler_factory = handlers[options?.kind ?? 'default'];
Expand Down
4 changes: 2 additions & 2 deletions packages/svelte/src/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Reexport your entry components here
import { didCancel, timeout } from './utils';
import { didCancel, timeout, transform } from './utils';
import { task, CancelationError } from './task.js';
export type { Task, SheepdogUtils, TaskInstance } from './task.js';

export { task, CancelationError, didCancel, timeout };
export { task, CancelationError, didCancel, timeout, transform };
7 changes: 2 additions & 5 deletions packages/svelte/src/lib/task.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { onDestroy } from 'svelte';
import { writable } from 'svelte/store';
import type { HandlerType, HandlersMap, SheepdogUtils, TaskOptions } from './core';
import type { HandlerType, HandlersMap, SheepdogUtils, TaskOptions, TaskFunction } from './core';
import { CancelationError, createTask, handlers } from './core';
import type { ReadableWithGet, WritableWithGet } from './internal/helpers';
import { writable_with_get } from './internal/helpers';
Expand All @@ -22,10 +22,7 @@ export type TaskInstance<TReturn = undefined> = {
};

function _task<TArgs = unknown, TReturn = undefined>(
gen_or_fun: (
args: TArgs,
utils: SheepdogUtils,
) => Promise<TReturn> | AsyncGenerator<unknown, TReturn, unknown>,
gen_or_fun: TaskFunction<TArgs, TReturn>,
options?: TaskOptions,
) {
const { subscribe, ...result } = writable({
Expand Down
Loading

0 comments on commit 69f253d

Please sign in to comment.