Creating your first plugin
We'll focus on a frontend-only plugin as that is the simplest to get started with.
Prerequisites
Before you begin this, you'll need:
- karrot-frontend development environment up and running
You can find some examples over at karrot/plugin-examples.
We'll use vite for this project, you can use other tools, but this is recommended.
Assuming you have karrot-frontend up and running elsewhere, go to a new directory, and we'll create your plugin project. It'll be a separate project to karrot-frontend, so don't create inside that folder.
yarn or npm or pnpm or bun or deno
There are lots of ways to run and develop JavaScript in 2024. We use yarn (classic) with Karrot, but I'm sure you can adapt this guide for another tool if you prefer it.
Initialize the project
yarn create vite
I chose these options (the Vue
option is important, the others you can choose what you want):
✔ Project name: … frontend-only
✔ Select a framework: › Vue
✔ Select a variant: › JavaScript
Now we can go into the directory we just created:
cd frontend-only
Install the dependencies:
yarn
Start the dev server to check it's working:
yarn dev
You should see something like this:
VITE v5.2.4 ready in 101 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h + enter to show help
When you visit http://localhost:5173/ you should see some content!
TIP
You might have a different address to visit check what it printed out for you
⚙️ Setup to be a plugin
We need to make some changes now to prepare it to be a Karrot plugin. This is the boring bit before we get round to the fun stuff.
Externalizing libraries
When you run your plugin, Karrot will make some libraries available. We don't want them to get included twice (once in Karrot and once in your plugin), so we externalize those libraries.
This means when your plugin is built, it won't include them as it ✨ knows ✨ those libraries will be provided externally.
First we need a vite plugin to help us out:
yarn add -D vite-plugin-externalize-dependencies
Then, open your vite.config.js
(or vite.config.ts
if you selected Typescript earlier), which should look approximately like this:
import { defineConfig } from "vite"
import vue from "@vitejs/plugin-vue"
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
})
And add code so it looks something like this:
import { defineConfig } from "vite"
import vue from "@vitejs/plugin-vue"
import externalize from "vite-plugin-externalize-dependencies"
// Libraries that will be provided by Karrot
// You can leave any out that you don't actually want to use
const externals = [
"vue",
"vue-router",
"quasar",
"axios",
"@tanstack/vue-query",
"@karrot/plugin",
]
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
// Karrot uses vue!
vue(),
// Externals support during development
externalize({ externals }),
],
build: {
// Karrot plugin system uses this manifest to know which files are in the plugin
manifest: true,
rollupOptions: {
// Entry point, this is the file that we'll load initially, should export a Karrot plugin
input: ["./src/main.js"],
// Externals support for build
external: externals,
// This is "library mode" and ensures we include the plugin in the export
preserveEntrySignatures: "strict",
},
},
})
Install externals locally
You might find it useful to have those externals also installed in your project, so your editor can offer you coding assistance. This is a good idea.
The externalize configuration only affects how it's exported.
So, you can install the dependencies locally with:
yarn add vue vue-router quasar axios @tanstack/vue-query
If you want to match the exact versions we use in Karrot you can check the karrot-frontend package.json and yarn.lock files.
For more casual use though, you should be fine with whatever it installs 😃. We tend to keep up with version updates.
When it runs it'll always use the same version as Karrot does, so there will be no conflicts - but if the versions are very different your plugin might not run.
🚧 In the future we might publish the @karrot/plugin
package and include type information.
🧹 Clean out the template files
The vite template gives you files we don't need, so you can remove:
index.html
- everything in
src
☁️ Dreaming ...
One day we could create our own template project, so you can just run yarn create karrot-plugin
😃
Define our plugin
Then we need to define our plugin in src/main.js
:
import { unref } from "vue"
import { setCssVar } from "quasar"
import HelloWorld from "./components/HelloWorld.vue"
import { useModifySidenav, useCurrentGroupService } from "@karrot/plugin"
// This is our plugin definition
export default {
// This will be called when the page first loads
// It's a quasar boot file, so you can read their docs
// https://quasar.dev/quasar-cli-vite/boot-files/
boot({ router }) {
// Change the colour scheme!
// You can see more options at https://quasar.dev/style/color-palette#how-it-works
setCssVar("primary", "#581845")
// Add a new page within the groups section
// This is just a normal route definition
// See https://router.vuejs.org/guide/advanced/dynamic-routing.html
router.addRoute("group", {
name: "hello-world",
path: "hello-world",
meta: {
breadcrumbs: [
{
name: "🌎",
},
],
},
component: HelloWorld,
})
},
// This is called when the app boots
// It works like a vue setup entrypoint
// See https://vuejs.org/api/composition-api-setup
setup() {
// We need to pass groupId for routes as we won't always
// have a groupId available in the current route (e.g. on the profile page)
// Note that it's a ref, so we need to unref it later or call .value on it
const { groupId } = useCurrentGroupService()
useModifySidenav((entries) => {
// Put our new menu item at the top
entries.splice(0, 0, {
name: "hello-world",
label: "Hello World",
icon: "fas fa-globe",
// This is the route name we want to link to
to: { name: "hello-world", params: { groupId: unref(groupId) } },
})
})
},
}
Not everything from quasar is available
You can check the list of things we export here src/plugin/quasar.js
Maybe in the future this changes 😃 If there is something you need, please ask!
Then create the <HelloWorld>
component in src/components/HelloWorld.vue
:
<template>
<h1>Hello World!</h1>
</template>
🔌 Connecting your plugin to the karrot-frontend
Ensure your plugin dev server is running. Now, we need to tell your karrot-frontend dev server about your plugin.
In the karrot-frontend directory, create or edit a .env
file to add the following line:
LOCAL_PLUGINS=http://localhost:5173/src/main.js
Check the address!
Check that is the correct address for your plugin dev server, as it might not always be that address.
Now go and open the karrot-frontend site, which should be at http://localhost:8080 and if you're logged in you should be able to use your new menu entry! 🥳
🧢 Recap
So, what we just did:
- created a new plugin project using vite
- configured vite to work for building a plugin rather than a whole app
- created a basic Karrot plugin
- connected it to our locally running karrot-frontend
What we didn't do:
- build the plugin ready to be installed in a Karrot instance
- use any data or APIs
- use Quasar components
That's all for now folks! 🐰 🥕 👋