Skip to content

Commit

Permalink
feat: plugins API (#40)
Browse files Browse the repository at this point in the history
Co-authored-by: Jacob Jenkins <[email protected]>
Co-authored-by: Luiz Ferraz <[email protected]>
  • Loading branch information
3 people authored Feb 10, 2024
1 parent 54d129f commit f05c7ab
Show file tree
Hide file tree
Showing 51 changed files with 1,219 additions and 438 deletions.
5 changes: 5 additions & 0 deletions .changeset/violet-pans-march.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"astro-integration-kit": minor
---

Introduces plugins and improves documentation
112 changes: 111 additions & 1 deletion docs/astro.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
import starlight from "@astrojs/starlight";
import { defineConfig } from "astro/config";
import { version } from "../package/package.json";

const badge = (type: "new" | "updated" | "soon") => ({
variant: {
new: "success" as const,
updated: "caution" as const,
soon: "tip" as const,
}[type],
text: {
new: "New",
updated: "Updated",
soon: "Soon",
}[type],
});

// https://astro.build/config
export default defineConfig({
Expand All @@ -12,11 +26,33 @@ export default defineConfig({
},
social: {
github: "https://github.com/florian-lefebvre/astro-integration-kit",
discord:
"https://discord.com/channels/830184174198718474/1197638002764152843",
},
editLink: {
baseUrl:
"https://github.com/florian-lefebvre/astro-integration-kit/edit/main/docs/",
},
customCss: ["./src/styles/main.css"],
head: [
{
tag: "link",
attrs: {
rel: "preconnect",
href: "https://rsms.me/",
},
},
{
tag: "link",
attrs: {
rel: "stylesheet",
href: "https://rsms.me/inter/inter.css",
},
},
],
expressiveCode: {
themes: ["one-dark-pro", "starlight-light"],
},
sidebar: [
{
label: "Getting started",
Expand All @@ -28,20 +64,94 @@ export default defineConfig({
{
label: "Usage",
link: "/getting-started/usage/",
badge: badge("updated"),
},
{
label: "Why Astro Integration Kit",
link: "/getting-started/why",
badge: badge("new"),
},
{
label: "Showcase",
link: "/getting-started/showcase/",
},
],
},
{
label: "Core",
items: [
{
label: "defineIntegration",
link: "/core/define-integration/",
badge: badge("updated"),
},
{
label: "defineOptions",
link: "/core/define-options/",
badge: badge("new"),
},
{
label: "createResolver",
link: "/core/create-resolver/",
},
{
label: "definePlugin",
link: "/core/define-plugin/",
badge: badge("new"),
},
],
},
{
label: "Utilities",
autogenerate: { directory: "utilities" },
items: [
{
label: "addDts",
link: "/utilities/add-dts/",
badge: badge("new"),
},
{
label: "addVirtualImport",
link: "/utilities/add-virtual-import/",
},
{
label: "addVitePlugin",
link: "/utilities/add-vite-plugin/",
},
{
label: "hasIntegration",
link: "/utilities/has-integration/",
badge: badge("updated"),
},
{
label: "watchIntegration (HMR)",
link: "/utilities/watch-integration/",
},
],
},
{
label: "Guides",
items: [
{
label: "Authoring an integration",
link: "/guides/authoring-an-integration",
badge: badge("soon"),
},
{
label: "Authoring a plugin",
link: "/guides/authoring-a-plugin",
badge: badge("soon"),
},
],
},
{
label: `v${version} changelog ↗`,
link: `https://github.com/florian-lefebvre/astro-integration-kit/blob/main/package/CHANGELOG.md#${version.replaceAll(
".",
"",
)}`,
attrs: {
target: "_blank",
},
},
],
lastUpdated: true,
Expand Down
5 changes: 5 additions & 0 deletions docs/src/components/Disabled.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div style="cursor:not-allowed">
<div style="opacity: .5;pointer-events:none">
<slot />
</div>
</div>
73 changes: 73 additions & 0 deletions docs/src/content/docs/core/create-resolver.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
---
title: createResolver
description: Allows resolving paths relatively to the integration folder easily.
---

`createResolver` allows you to resolve paths relative to the integration folder easily.

```ts title="integration/index.ts" "createResolver" "resolve"
import type { AstroIntegration } from "astro";
import { createResolver } from "astro-integration-kit";

export default function myIntegration(): AstroIntegration {
const { resolve } = createResolver(import.meta.url);

return {
name: "my-integration",
hooks: {
"astro:config:setup": ({ addDevToolbarApp }) => {
addDevToolbarApp(resolve("./plugin.ts"));
}
}
}
}
```

## Why should you use it?

We think it provides a better DX. Instead of having to do some back and forth between
your code and your `package.json` `exports` fields, you can just use some intuitive
relative paths!

```json title="package.json" del={4}
{
"name": "package-name",
"exports": {
"pages/my-route.astro": "./src/pages/my-route.astro",
"plugin.ts": "./src/plugin.ts"
}
}
```

```ts title="integration/index.ts" del={12,16} ins={5,13,17}
import type { AstroIntegration } from "astro";
import { createResolver } from "astro-integration-kit";

export default function myIntegration(): AstroIntegration {
const { resolve } = createResolver(import.meta.url);
return {
name: "my-integration",
hooks: {
"astro:config:setup": ({ injectRoute, addDevToolbarApp }) => {
injectRoute({
pattern: "/my-route",
entrypoint: "package-name/pages/my-route.astro"
entrypoint: resolve("./pages/my-route.astro")
})
addDevToolbarApp(
"package-name/plugin.ts"
resolve("./plugin.ts")
)
}
}
}
}
```

## Usage

Always pass `import.meta.url` to `createResolver`! That's the equivalent of the
old `__filename`. This way, `resolve` will always create a valid path relatively,
no matter its location in `node_modules`, package managers madness etc

We recommend calling it in `setup` to easily access `resolve` in any hook.
153 changes: 153 additions & 0 deletions docs/src/content/docs/core/define-integration.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
---
title: defineIntegration
description: A powerful wrapper around the standard Astro Integrations API. It allows to provide extra hooks, functionality and best-practices when creating Astro Integrations.
---
import Disabled from "~/components/Disabled.astro"
import { LinkCard } from '@astrojs/starlight/components';

`defineIntegration` is a powerful wrapper around the standard Astro Integrations API. It allows to provide
extra hooks, functionality and best-practices when creating Astro Integrations.

```ts title="my-integration/index.ts" "defineIntegration"
import {
createResolver,
defineIntegration,
defineOptions,
} from "astro-integration-kit";
import { corePlugins } from "astro-integration-kit/plugins";

type Options = {
foo?: string | undefined;
}

export default defineIntegration({
name: "my-integration",
options: defineOptions<Options>({ foo: "bar" }),
plugins: [...corePlugins],
setup({ options }) {
const { resolve } = createResolver(import.meta.url);

return {
"astro:config:setup": ({ watchIntegration }) => {
watchIntegration(resolve())
}
}
}
})
```

## Defining an integration

To define an integration, use the `defineIntegration` utility:

```ts title="my-integration/index.ts" "defineIntegration"
import { defineIntegration } from "astro-integration-kit";

export default defineIntegration({
// ...
name: "my-integration"
})
```

:::caution
Never pass generics manually to `defineIntegration` or it will break all the
TypeScript magic of options and plugins.
:::

<Disabled>
<LinkCard
title="Authoring an integration (soon)"
description="Learn how to write an integration the right way using Astro Integration Kit."
href="#"
/>
</Disabled>

It accepts a few arguments, whose usage is explained in the sections below.

## Handling options and defaults

`defineIntegration` accepts an `option` argument that you need to use with `defineOptions`.

```ts title="my-integration/index.ts" "defineOptions" ins={3-5,9}
import { defineIntegration, defineOptions } from "astro-integration-kit";

type Options = {
foo?: string | undefined;
}

export default defineIntegration({
// ...
options: defineOptions<Options>({ foo: "bar" })
})
```

:::note
Really read the docs about `defineOptions` through the link below, there are limitations and stuff
you need to know about.
:::

<LinkCard
title="Read more about defineOptions"
description="Allows defining an integration options while keeping the whole thing type-safe."
href="/core/define-options/"
/>

## Using plugins

Plugins allow us (and you!) to make Astro Integration Kit really powerful. It allows to add arguments
to built-in hooks in a type-safe way. Get started by using our `corePlugins`:

```ts title="my-integration/index.ts" ins={2,6}
import { defineIntegration } from "astro-integration-kit";
import { corePlugins } from "astro-integration-kit/plugins";

export default defineIntegration({
// ...
plugins: [...corePlugins]
})
```

Under the hood, those plugins define using `definePlugin` pass "metadata" as types thanks to generics.

<Disabled>
<LinkCard
title="Authoring a plugin (soon)"
description="Learn how to write an Astro Integration Kit plugin the right way."
href="#"
/>
</Disabled>

## Adding the logic with `setup`

If you've written an integration before by returning an `AstroIntegration` from a function, it's exactly
the same with `setup`! This is where all the logic lives:

```ts title="my-integration/index.ts" ins={5-7}
import { defineIntegration } from "astro-integration-kit";

export default defineIntegration({
// ...
setup() {
return {}
}
})
```
It accepts an object with data from the integration definition:

```ts title="my-integration/index.ts" "{ options, name }"
import { defineIntegration } from "astro-integration-kit";

export default defineIntegration({
// ...
setup({ options, name }) {
return {}
}
})
```

In setup, you'll want to add any logic that is shared between any hook, for example:

- Calling `createResolver`
- Save the `config` from `astro:config:done` to be used in later hooks

It needs to return Astro hooks.
Loading

0 comments on commit f05c7ab

Please sign in to comment.