Skip to content

Upgrade guide

Features get added and removed, and breaking changes are introduced! This documents how to migrate.

0.16.0


Improved type inference for defineUtility

Previously the defineUtility signature would emit a type that required naming internal Astro types in some cases, which caused error TS2742 about non-portable types. This type was improved to emit small and portable types through a new public HookUtility type exported from astro-integration-kit.

The generic arguments of defineUtility haven’t changed, no action is necessary for you code to benefit from this change.

As an example, an utility defined with the following code:

1
export const utility = defineUtility("astro:config:setup")(
2
(params: HookParameters<"astro:config:setup">, options: { name: string }) => {
3
// do something
4
}
5
);

Was previously emitted as:

1
import * as astro from "astro";
2
import { AstroIntegrationLogger } from "astro";
3
4
type DeepPartial<T> = {
5
[P in keyof T]?: T[P] extends (infer U)[]
6
? DeepPartial<U>[]
7
: T[P] extends object | undefined
8
? DeepPartial<T[P]>
9
: T[P];
10
};
11
12
export const utility: (
13
params: {
14
config: astro.AstroConfig;
15
command: "dev" | "build" | "preview";
16
isRestart: boolean;
17
updateConfig: (
18
newConfig: DeepPartial<astro.AstroConfig>
19
) => astro.AstroConfig;
20
addRenderer: (renderer: astro.AstroRenderer) => void;
21
addWatchFile: (path: URL | string) => void;
22
injectScript: (stage: astro.InjectedScriptStage, content: string) => void;
23
injectRoute: (injectRoute: astro.InjectedRoute) => void;
24
addClientDirective: (directive: astro.ClientDirectiveConfig) => void;
25
addDevOverlayPlugin: (entrypoint: string) => void;
26
addDevToolbarApp: (entrypoint: astro.DevToolbarAppEntry | string) => void;
27
addMiddleware: (mid: astro.AstroIntegrationMiddleware) => void;
28
logger: AstroIntegrationLogger;
29
},
30
options: {
31
name: string;
32
}
33
) => void;

Now it is emitted as:

1
import * as astro_integration_kit from "astro-integration-kit";
2
3
export const utility: astro_integration_kit.HookUtility<
4
"astro:config:setup",
5
[
6
options: {
7
name: string;
8
}
9
],
10
void
11
>;

Improved emitted types for defineIntegration

Previously the defineUtility signature would emit a type that required naming internal Astro types in some cases, which caused error TS2742 about non-portable types. This type was improved to not emit the portion of the type that would be discarded by its own construction.

To achieve this with newer versions of TS (>=5.5), the generic parameters had to be updated. If you were passing type parameters explicitly instead of relying on type inference you should either remove the parameters or adapt your types to the new parameter requirements.

As an example, an integration defined with the following code:

1
export const integration defineIntegration({
2
name: 'some-integration',
3
setup: () => ({
4
hooks: {
5
'astro:config:setup': (params) => {
6
// do something
7
},
8
},
9
something: (it: string): string => it,
10
}),
11
});

Was previously emitted as:

1
import * as astro from "astro";
2
import { AstroIntegrationLogger } from "astro";
3
4
type Prettify<T> = {
5
[K in keyof T]: T[K];
6
} & {};
7
8
type DeepPartial<T> = {
9
[P in keyof T]?: T[P] extends (infer U)[]
10
? DeepPartial<U>[]
11
: T[P] extends object | undefined
12
? DeepPartial<T[P]>
13
: T[P];
14
};
15
16
export const integration: () => astro.AstroIntegration &
17
Prettify<
18
Omit<
19
ReturnType<
20
() => {
21
hooks: {
22
"astro:config:setup": (params: {
23
config: astro.AstroConfig;
24
command: "dev" | "build" | "preview";
25
isRestart: boolean;
26
updateConfig: (
27
newConfig: DeepPartial<astro.AstroConfig>
28
) => astro.AstroConfig;
29
addRenderer: (renderer: astro.AstroRenderer) => void;
30
addWatchFile: (path: URL | string) => void;
31
injectScript: (
32
stage: astro.InjectedScriptStage,
33
content: string
34
) => void;
35
injectRoute: (injectRoute: astro.InjectedRoute) => void;
36
addClientDirective: (
37
directive: astro.ClientDirectiveConfig
38
) => void;
39
addDevOverlayPlugin: (entrypoint: string) => void;
40
addDevToolbarApp: (
41
entrypoint: astro.DevToolbarAppEntry | string
42
) => void;
43
addMiddleware: (mid: astro.AstroIntegrationMiddleware) => void;
44
logger: AstroIntegrationLogger;
45
}) => void;
46
};
47
something: (it: string) => string;
48
}
49
>,
50
keyof astro.AstroIntegration
51
>
52
>;

Now it is emitted as:

1
import * as astro from "astro";
2
3
export const integration: () => astro.AstroIntegration & {
4
something: (it: string) => string;
5
};

0.15.0


Native hook types

With the new minimal version of Astro, there is no longer any need for the previous workaround to support non-native hooks, like those from @astrojs/db. The following changes are needed to replace the previous Astro Integration Kit types with the native types:

  • Hook type extensions
    1
    namespace AstroIntegrationKit {
    2
    interface ExtraHooks {
    3
    namespace Astro {
    4
    interface IntegrationHooks {
    5
    'myLib:myHook': (params: { foo: string }) => Promise<void>;
    6
    }
    7
    }
  • Hook parameters
    1
    import type { HookParameters } from 'astro-integration-kit';
    2
    import type { HookParameters } from 'astro';
  • @astrojs/db hooks:
    1
    import "astro-integration-kit/types/db";
    2
    /// <reference types="@astrojs/db" />
    If you are using @astrojs/db values and types in your project, you can just remove import. Importing anything from the library also loads the types.
    1
    import "astro-integration-kit/types/db";
    2
    import {...} from "@astrojs/db";

0.13.0


Plugin type simplified

The new Plugin signature added back in 0.9.0 returned the need for emitted declarations to include the full type for hook parameters from Astro (which was removed on 0.7.0). This was added along with support for a plugin to define new utilities for multiple hooks at once.

Now, the Plugin generics have been simplified again to allow for such support without having to replicate Astro’s hook parameters types. Just like in 0.7.0, this should be non-breaking for any plugin relying on type inference but plugins with explicitly declared signatures need to update the following:

1
type SomePlugin = Plugin<
2
"utilityName",
3
"astro:config:setup",
4
{
5
"astro:config:setup": (HookParameters<"astro:config:setup">) => {
6
"astro:config:setup": {
7
utilityName: (params: UtilityParams) => UtilityOutput
8
}
9
}
10
>;

0.12.0


addDevToolbarFrameworkApp removed

The addDevToolbarFrameworkApp has been removed in favour of the Astro defineToolbarApp utility added in Astro v4.7.0.

To migrate your Dev Toolbar App to the new Astro utility, please see their documentation here.

0.11.0


defineIntegration setup return type updated

Previously the return of the setup function passed to defineIntegration was the Astro hooks defined by the integration, and would be set as the hooks property in the final integration object.

Now, the expected return of setup is the properties of the integration object itself:

my-integration.ts
1
import { defineIntegration } from "astro-integration-kit";
2
3
export default defineIntegration({
4
name: "my-integration",
5
setup({ name }) {
6
return {
7
hooks: {
8
"astro:config:setup": () => {
9
// ...
10
},
11
},
12
};
13
},
14
});

0.10.0


watchIntegration renamed to watchDirectory

Since watchIntegration is not recommended for dev HMR anymore, it has been renamed according to what it really does, watching a directory:

1
import { watchIntegration } from "astro-integration-kit";
2
watchIntegration(params, resolve());
3
import { watchDirectory } from "astro-integration-kit";
4
watchDirectory(params, resolve());

0.9.0


New withPlugins utility, plugins removed from defineIntegration

Plugins are no longer passed to defineIntegration. Instead, there’s a new withPlugins utility.

my-integration/index.ts
1
import { defineIntegration, withPlugins } from "astro-integration-kit";
2
import { hasVitePluginPlugin } from "astro-integration-kit/plugins";
3
4
export default defineIntegration({
5
name: "my-integration",
6
plugins: [hasVitePluginPlugin],
7
setup({ name }) {
8
return {
9
"astro:config:setup": ({ hasVitePlugin }) => {},
10
};
11
return withPlugins({
12
name,
13
plugins: [hasVitePluginPlugin],
14
hooks: {
15
"astro:config:setup": ({ hasVitePlugin }) => {},
16
},
17
});
18
},
19
});

New Plugin signature (and definePlugin)

You should not use the Plugin type manually but if you do, you’ll need to update it:

1
type SomePlugin = Plugin<
2
"utilityName",
3
"astro:config:setup",
4
(params: UtilityParams) => UtilityOutput,
5
{
6
"astro:config:setup": (HookParameters<"astro:config:setup">) => {
7
utilityName: (params: UtilityParams) => UtilityOutput
8
}
9
}
10
>;

The definePlugin signature has been updated as well:

1
import type { AstroConfig } from "astro";
2
import type { Plugin, PluginOption } from "vite";
3
import { definePlugin } from "astro-integration-kit";
4
import { hasVitePlugin } from "../utilities/has-vite-plugin.js";
5
6
export const hasVitePluginPlugin = definePlugin({
7
name: "hasVitePlugin",
8
hook: "astro:config:setup",
9
implementation: (params) => (plugin: string | PluginOption) =>
10
hasVitePlugin(params, { plugin }),
11
setup() {
12
return {
13
"astro:config:setup": (params) => ({
14
hasVitePlugin: (plugin: string | PluginOption) =>
15
hasVitePlugin(params, { plugin }),
16
}),
17
};
18
},
19
});

0.8.0


Removed plugins

The following plugins have been removed in favor of their standalone utility version:

  • addDevToolbarFrameworkAppPlugin
  • addDtsPlugin
  • addIntegrationPlugin
  • addVirtualImportsPlugin
  • addVitePluginPlugin
  • hasIntegrationPlugin
  • injectDevRoutePlugin
  • watchIntegrationPlugin

Only hasVitePluginPlugin remains.

corePlugins removed

corePlugins is no longer exported from astro-integration-kit. Import the plugin you want directly:

1
import { defineIntegration } from "astro-integration-kit";
2
import {
3
corePlugins,
4
hasVitePluginPlugin,
5
} from "astro-integration-kit/plugins";
6
7
export default defineIntegration({
8
// ...
9
plugins: [...corePlugins],
10
plugins: [hasVitePluginPlugin],
11
});

Updated utilities

addDevToolbarFrameworkApp

1
const { resolve } = createResolver(import.meta.url);
2
3
addDevToolbarFrameworkApp({
4
framework: "vue",
5
name: "Test Vue Plugin",
6
id: "my-vue-plugin",
7
icon: `<svg version="1.1" viewBox="0 0 261.76 226.69" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(1.3333 0 0 -1.3333 -76.311 313.34)"><g transform="translate(178.06 235.01)"><path d="m0 0-22.669-39.264-22.669 39.264h-75.491l98.16-170.02 98.16 170.02z" fill="#41b883"/></g><g transform="translate(178.06 235.01)"><path d="m0 0-22.669-39.264-22.669 39.264h-36.227l58.896-102.01 58.896 102.01z" fill="#34495e"/></g></g></svg>`,
8
src: resolve("./my-plugin.vue"),
9
style: `
10
h1 {
11
font-family: Inter;
12
}
13
`,
14
config,
15
updateConfig,
16
addDevToolbarApp,
17
injectScript,
18
});
19
addDevToolbarFrameworkApp(params, {
20
framework: "vue",
21
name: "Test Vue Plugin",
22
id: "my-vue-plugin",
23
icon: `<svg version="1.1" viewBox="0 0 261.76 226.69" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(1.3333 0 0 -1.3333 -76.311 313.34)"><g transform="translate(178.06 235.01)"><path d="m0 0-22.669-39.264-22.669 39.264h-75.491l98.16-170.02 98.16 170.02z" fill="#41b883"/></g><g transform="translate(178.06 235.01)"><path d="m0 0-22.669-39.264-22.669 39.264h-36.227l58.896-102.01 58.896 102.01z" fill="#34495e"/></g></g></svg>`,
24
src: resolve("./my-plugin.vue"),
25
style: `
26
h1 {
27
font-family: Inter;
28
}
29
`,
30
});

addDts

1
addDts({
2
name: "my-integration",
3
content: `declare module "virtual:my-integration" {}`,
4
root: config.root,
5
srcDir: config.srcDir,
6
logger,
7
});
8
addDts(params, {
9
name: "my-integration",
10
content: `declare module "virtual:my-integration" {}`,
11
});

addIntegration

1
addIntegration({
2
integration: Vue(),
3
updateConfig,
4
config,
5
logger,
6
});
7
addIntegration(params, {
8
integration: Vue(),
9
});

addVirtualImports

1
addVirtualImports({
2
name: "my-integration",
3
imports: {
4
"virtual:my-integration/config": `export default ${JSON.stringify({
5
foo: "bar",
6
})}`,
7
},
8
updateConfig,
9
config,
10
});
11
addVirtualImports(params, {
12
name: "my-integration",
13
imports: {
14
"virtual:my-integration/config": `export default ${JSON.stringify({
15
foo: "bar",
16
})}`,
17
},
18
});

addVitePlugin

1
addVitePlugin({
2
plugin: VitePWA({ registerType: "autoUpdate" }),
3
config,
4
logger,
5
updateConfig,
6
});
7
addVitePlugin(params, {
8
plugin: VitePWA({ registerType: "autoUpdate" }),
9
});

hasVitePlugin

1
hasVitePlugin({
2
plugin: "vite-plugin-my-integration",
3
config,
4
});
5
hasVitePlugin(params, {
6
plugin: "vite-plugin-my-integration",
7
});

hasIntegration

1
hasIntegration({
2
name: "@astrojs/tailwind",
3
position: "before",
4
relativeTo: "my-integration",
5
config,
6
});
7
hasIntegration(params, {
8
name: "@astrojs/tailwind",
9
position: "before",
10
relativeTo: "my-integration",
11
});

injectDevRoute

1
const { resolve } = createResolver(import.meta.url);
2
3
injectDevRoute({
4
command,
5
injectRoute,
6
injectedRoute: {
7
pattern: "/foo",
8
entrypoint: resolve("./pages/foo.astro"),
9
},
10
});
11
injectDevRoute(params, {
12
pattern: "/foo",
13
entrypoint: resolve("./pages/foo.astro"),
14
});

watchIntegration

1
const { resolve } = createResolver(import.meta.url);
2
3
watchIntegration({
4
addWatchFile,
5
command,
6
dir: resolve(),
7
updateConfig,
8
});
9
watchIntegration(params, resolve());

0.7.0


Updated options and optionsSchema

They’re not optional by default, you need to manually add .optional() at the end of your zod schema. If it’s optional, users can still pass nothing or undefined.

1
defineIntegration({
2
// ...
3
optionsSchema: z.object({ foo: z.string() }),
4
optionsSchema: z.object({ foo: z.string() }).optional(),
5
});

Plugins types

Plugin generics have been simplified, allowing simpler plugin builds. This should be non-breaking for plugin relying on type inference, plugins with explicitly declared signature should update the following:

1
type SomePlugin = Plugin<
2
"utilityName",
3
"astro:config:setup",
4
(p: HookParams) => (params: UtilityParams) => UtilityOutput
5
(params: UtilityParams) => UtilityOutput
6
>;
7
8
export const somePlugin: SomePlugin = definePlugin();

0.6.0


Updated addVitePlugin

The addVitePlugin utility now requires a config and logger parameter to log warnings for duplicate plugins

1
"astro:config:setup": ({ config, updateConfig }) => {
2
addVitePlugin({
3
plugin,
4
config,
5
logger
6
updateConfig
7
})
8
}

Or you can turn off warnings for duplicate plugins using warnDuplicate: false

1
addVitePlugin({
2
warnDuplicate: false,
3
plugin,
4
updateConfig,
5
});

Updated addVirtualImports

The addVirtualImports utility now requires a config parameter

1
"astro:config:setup": ({ config, updateConfig }) => {
2
addVirtualImports({
3
updateConfig,
4
config
5
name: 'my-integration',
6
imports: {
7
'virtual:my-integration/config': `export default ${JSON.stringify({ foo: "bar" })}`,
8
},
9
})
10
}

Updated addDevToolbarFrameworkApp

The addDevToolbarFrameworkApp utility now requires a config parameter

1
"astro:config:setup": ({ config, updateConfig }) => {
2
addDevToolbarFrameworkApp({
3
config,
4
framework: "vue",
5
name: "Test Vue Plugin",
6
id: "my-vue-plugin",
7
icon: `<svg>...</svg>`,
8
src: resolve("./my-plugin.vue"),
9
style: `
10
h1 {
11
font-family: Inter;
12
}
13
`,
14
})
15
}

0.5.0


Updated addVirtualImport

addVirtualImport was removed in 0.5.0. Here is how to migrate:

my-integration/index.ts
1
import { defineIntegration } from "astro-integration-kit";
2
import { addVirtualImportPlugin } from "astro-integration-kit/plugins";
3
import { addVirtualImportsPlugin } from "astro-integration-kit/plugins";
4
5
export default defineIntegration({
6
name: "my-integration",
7
plugins: [addVirtualImportPlugin],
8
plugins: [addVirtualImportsPlugin],
9
setup() {
10
return {
11
"astro:config:setup": ({ addVirtualImport }) => {
12
"astro:config:setup": ({ addVirtualImports }) => {
13
addVirtualImport({
14
name: 'virtual:my-integration/config',
15
content: `export default ${JSON.stringify({ foo: "bar" })}`,
16
})
17
addVirtualImport({
18
name: 'virtual:my-integration/context',
19
content: `export default ${JSON.stringify({ entrypoint: import.meta.url })}`,
20
})
21
addVirtualImports({
22
'virtual:my-integration/config': `export default ${JSON.stringify({ foo: "bar" })}`,
23
'virtual:my-integration/context': `export default ${JSON.stringify({ entrypoint: import.meta.url })}`
24
})
25
}
26
}
27
}
28
})

0.2.0


Removed defineOptions

defineOptions has been removed in 0.2.0. Here is how to migrate:

my-integration/index.ts
1
import { defineIntegration, defineOptions } from "astro-integration-kit";
2
import { defineIntegration } from "astro-integration-kit";
3
import { z } from "astro/zod";
4
5
type Options = {
6
/**
7
* A comment
8
*
9
* @default `"bar"`
10
*/
11
foo?: string | undefined;
12
};
13
14
export default defineIntegration({
15
// ...
16
options: defineOptions<Options>({ foo: "bar" }),
17
optionsSchema: z.object({
18
/**
19
* A comment
20
*
21
* @default `"bar"`
22
*/
23
foo: z.string().optional().default("bar"),
24
}),
25
});