Skip to content

Modular Station - API

This utility allows you, as an integration author, to expose APIs for other integrations that might be in use alogside yours. Instead of requiring them to call addIntegration instantiating yours, which has multiple limitations, they can use the API from your integration regardless of how it was installed.

Installing the dependency

Terminal window
npx astro add @inox-tools/modular-station

How to use as an author

Assuming you already have an integration, you can just wrap it in the withApi function to give it superpowers:

my-integration.ts
import { withApi } from '@inox-tools/modular-station';
export default (options: Options) => {
export default withApi((options: Options) => {
const sharedState = ...;
return {
hooks: {
// ... using the shared state
},
};
};
});

With this, you can now expose APIs alongside your hooks:

my-integration.ts
import { withApi } from '@inox-tools/modular-station';
export default withApi((options: Options) => {
const sharedState = ...;
return {
hooks: {
// ... using the shared state
},
addSomething(thing: string) {
// add the thing to shared state
},
};
});

onHook

By default, accessing your API from any hook besides astro:config:setup using the AIK plugin will throw an error. This is because that is the only hook where nearly everything can be done in Astro’s lifecycle.

If you have an API that also works when called from other hooks, you can wrap them with onHook to declare which hooks are supported. For example, if you expose a method to register information that is only used on astro:build:start you can allow it to be used on any hook before that:

my-integration.ts
import { withApi, onHook } from '@inox-tools/modular-station';
export default withApi((options: Options) => {
const sharedState = ...;
return {
hooks: {
// ... using the shared state
},
addSomething: onHook([
'astro:config:setup',
'astro:config:done',
'astro:build:setup',
], (thing: string) {
// add the thing to shared state
}),
};
});

How to use as a consumer

To use the APIs provided by an integration on your own, you have multiple options.

Without Astro Integration Kit (the boring way)

If you don’t use Astro Integration Kit, first off you should, but if you really don’t want to have an easy life, read ahead.

On your astro:config:setup or astro:config:done hooks you can get the API from the provided AstroConfig like so:

my-integration.ts
import otherIntegration from 'other-integration';
export default () => {
return {
name: 'my-integration',
hooks: {
'astro:config:setup': ({ config }) => {
const api = otherIntegration.fromConfig(config);
api?.addSomething();
},
},
};
};

The API might be null if the integration is not present. If needed, you can instantiate and add the integration yourself or use the optional API.

With Astro Integration Kit (the fun way)

Integration providing APIs using Modular Station can be used as AIK Plugins. You can use as either an optional plugin or a required plugin.

Optional plugin

You can call asOptionalPlugin on integrations using Modular Station to get an AIK Plugin that exposes that integration’s API under a name of your choosing. If the integration is not present, the value in your hooks will be null.

Contrary to the method without AIK, this allows the API to be used on any hook:

my-integration.ts
import { withPlugins } from 'astro-integration-kit';
import otherIntegration from 'other-integration';
export default () => {
return withPlugins({
name: 'my-integration',
plugins: [otherIntegration.asOptionalPlugin('otherApi')]
hooks: {
'astro:config:setup': ({ otherApi }) => {
otherApi?.addSomething();
},
'astro:server:start': ({ otherApi }) => {
otherApi?.addSomething();
},
},
};
};

Required plugin

You can call asPlugin on integrations using Modular Station to get an AIK Plugin that exposes that integration’s API under a name of your choosing. If the integration is not present, it will be added using the provided options. You must always provide the options even if it is already installed because that check happens later.

my-integration.ts
import { withPlugins } from 'astro-integration-kit';
import otherIntegration from 'other-integration';
export default () => {
return withPlugins({
name: 'my-integration',
plugins: [otherIntegration.asPlugin('otherApi', {foo: 'bar'})]
hooks: {
'astro:config:setup': ({ otherApi }) => {
otherApi.addSomething();
},
'astro:server:start': ({ otherApi }) => {
otherApi.addSomething();
},
},
};
};