Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: plugins API #40

Merged
merged 40 commits into from
Feb 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
182ffd8
feat: exeperiment with types
florian-lefebvre Feb 1, 2024
e4afc35
feat: work on overridable plugins
florian-lefebvre Feb 4, 2024
1b0caca
feat: rework options
florian-lefebvre Feb 4, 2024
c5fc64b
fix: plugins type inference
florian-lefebvre Feb 4, 2024
21a8f91
feat: add addVirtualImportPlugin
florian-lefebvre Feb 4, 2024
5ac53ca
feat: add addVitePluginPlugin
florian-lefebvre Feb 4, 2024
d678e67
feat: add hasIntegrationPlugin
florian-lefebvre Feb 4, 2024
e592b41
feat: add watchIntegrationPlugin
florian-lefebvre Feb 4, 2024
c0846ba
chore: cleanup
florian-lefebvre Feb 4, 2024
f3a4c14
feat: add jsdoc for defineOptions and definePlugin
florian-lefebvre Feb 4, 2024
6fa731e
Merge branch 'main' into feat/plugins-api
florian-lefebvre Feb 4, 2024
1b22f12
docs: start update
florian-lefebvre Feb 4, 2024
a59be59
feat: work on docs
florian-lefebvre Feb 4, 2024
61315c5
feat: customize docs
florian-lefebvre Feb 4, 2024
8f3f7d5
feat: work on docs
florian-lefebvre Feb 5, 2024
804bbb7
feat: work on docs
florian-lefebvre Feb 5, 2024
9798241
feat: work on docs
florian-lefebvre Feb 5, 2024
1cb1252
feat: work on docs
florian-lefebvre Feb 5, 2024
97a9770
feat: work on docs
florian-lefebvre Feb 5, 2024
9c746b2
feat: work on tests
florian-lefebvre Feb 5, 2024
e807475
chore: add changeset
florian-lefebvre Feb 5, 2024
042b321
Update docs/src/content/docs/core/create-resolver.mdx
florian-lefebvre Feb 6, 2024
fd5bbba
Update docs/src/content/docs/core/define-integration.mdx
florian-lefebvre Feb 6, 2024
fcf1c47
Update docs/src/content/docs/core/define-options.mdx
florian-lefebvre Feb 6, 2024
09fba74
Update docs/src/content/docs/core/define-options.mdx
florian-lefebvre Feb 6, 2024
9b9c6f3
Update docs/src/content/docs/core/define-plugin.mdx
florian-lefebvre Feb 6, 2024
4087ce5
Update docs/src/content/docs/getting-started/usage.mdx
florian-lefebvre Feb 6, 2024
8b7b3b6
Update docs/src/content/docs/core/define-options.mdx
florian-lefebvre Feb 6, 2024
7d4c9fb
feat: update defineIntegration description
florian-lefebvre Feb 6, 2024
cbadbc3
Update docs/src/content/docs/core/define-options.mdx
florian-lefebvre Feb 6, 2024
2d939d7
fix: do not loose default hooks params type
florian-lefebvre Feb 6, 2024
f5f0151
docs: add comments to types
florian-lefebvre Feb 8, 2024
ad47674
Update define-integration.ts
florian-lefebvre Feb 9, 2024
dc3cadb
Update package/src/core/types.ts
florian-lefebvre Feb 9, 2024
2557059
Update package/src/internal/types.ts
florian-lefebvre Feb 10, 2024
084251b
feat: add comment
florian-lefebvre Feb 10, 2024
141a535
chore: try fix jsdoc
florian-lefebvre Feb 10, 2024
addcd34
chore: add new line
florian-lefebvre Feb 10, 2024
6e140cd
chore: temp
florian-lefebvre Feb 10, 2024
7bc1d6b
chore: try fix indentation
florian-lefebvre Feb 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading