- Opinionated Vue 3 Components Library Starter Template
- Also with Vue 3 Composables Library support
- A monorepo with multiple version-controlled packages
- TypeScript support
- 💨 Unocss - The instant on-demand Atomic CSS engine. Next generation utility css with css icons and many useful features.
- 🏛️ Iconify - Universal icon framework with 100+ icon sets, 100,000+ icons.
- 🪀 Vitepress - Vite & Vue Powered Static Site Generator (for documents site)
- ⌛ Vue Eslint TypeScript - Lint for typescript/javascript
- 💯 Prettier - Opinionated Code Formatter
- ♨️ Vitest - A blazing fast unit test framework powered by Vite
- 🎈 Testing Library - Simple and complete testing utilities for testing user interactions
- 🏓 Happy DOM - A JavaScript implementation of a web browser without its graphical user interface
- 🏗️ unplugin-auto-import - Auto import API (for javascript/typescript exports)
- 🎯 unplugin-vue-components - On-demand components auto importing for Vue (for vue components)
- 🪅 Pug - Write simpler html
- 🧩 Ni - Automatically use the right package manager (npm, yarn, pnpm) based on package lock file
- ⛸️ Rimraf - The UNIX command "rm -rf" for node
- 🛶 tsx - Node.js enhanced with esbuild to run TypeScript & ESM files
- 🛠️ Husky - Modern native Git hooks made easy
- 🤹 CommitLint - Helps your team adhering to a commit convention
- 🏒 Lint Staged - Run linters on git staged files
- 🥅 Release Please - Generate release PRs based on the conventionalcommits.org spec, automates CHANGELOG generation, the creation of GitHub releases, and version bumps
- 🚀 changelogithub - Generate changelog for GitHub releases from Conventional Commits
- Wait for release-please to support pnpm in order for "node-workspace" plugin to work. This feature to be released
git clone https://github.com/agufaui/vue-components-lib-starter-template.git
install @antfu/Ni
npm i -g @antfu/ni
ni
nr dev
GITHUB_TOKEN will not allow workflow to be triggered by another workflow, because it might cause loops, however, it kills the "auto" part of CI transit to CD. You'll need to create a PCA (Personal Access Token) or Deploy keys with only "public_repo" access under "Repo". PCA can also be set on organization level, so basically go to repository or organization Settings -> Secret -> Actions, create a token named "WORKFLOW_TOKEN"
Manual operation required to prevent loops, see below Release-Please section
- If you change
${{secrets.GITHUB_TOKEN}}
in .github/workflows/ci.yml to your PAT (personal access token), etc.${{secrets.GH_TOKEN}}
, you can trigger workflow from another workflow, it may cause loops though, etc. trigger same workflow (trigger workflow A from workflow A)
Go to NPM, get your token, then set it at organization level or repository level.
.
└── .github # Github files
└── workflows # Github workflow files
└── ci.yml # Github ci workflow
├── .husky # Husky files folder
└── packages # Multiple packages folder
├── .vitepress # Vitepress config and theme files (this is your documents site)
├── core # Your core vue components library source files (this is a package)
├── use # Your vue composables library source files, used by your core components library (this is a package)
└── index.md # Entry file for vitepress
Your final build files will be in "dist" folder under these 3 folders:
- .vitepress/dist (deploy to a static web server)
- core/dist (publish to npm registry)
- use/dist (publish to npm registry)
change all versions to 0.0.1
{
"packages/core": "0.0.1",
"packages/use": "0.0.1",
".": "0.0.1"
}
{
"packages": {
"packages/core": {
"releaseType": "node",
"draft": false,
"prerelease": false,
"bumpMinorPreMajor": true,
"bumpPatchForMinorPreMajor": true,
"changelogPath": "CHANGELOG.md",
"versioning": "default"
},
"packages/use": {
"releaseType": "node",
"skip-github-release": true,
"draft": false,
"prerelease": false,
"bumpMinorPreMajor": true,
"bumpPatchForMinorPreMajor": true,
"changelogPath": "CHANGELOG.md",
"versioning": "default"
},
".": {
"releaseType": "node",
"skip-github-release": true,
"draft": false,
"prerelease": false,
"bumpMinorPreMajor": true,
"bumpPatchForMinorPreMajor": true,
"changelogPath": "CHANGELOG.md",
"versioning": "default"
}
}
}
need to use single quote explicitly, double quote will cause error on exclamation mark "!":
git commit -m 'feat!: new feat'
- Multiple version-controlled packages: Manifest Driven release-please
- Single package: Release Please Action
This Template uses multiple version-controlled packages, but you can convert to single package easily:
- Remove two release-please json files mentioned above
- Change .github/workflows/ci.yml (also make release-please update all package.json versions)
- remove config files and index.ts in use folder
- change scripts/publish.ts
- Release-please will auto create a commit for Pull Request to auto update changelog and version, so you need to do a "git pull" after every release. If you forgt to pull, and there are changes in your local, conficts will occur, it's safe to merge, but will create a new merge commit. If you don't want that merge commit to pollute commit history, you probably want to do a rebase pull with stash option (stash is to preserve uncommitted changes). However, your local commits must be truly local, if some of them are already pushed to remote, and someone else has it, you probably don't want to do a rebase. Here are the rebase commands:
git pull --rebase <remote-name> <branch-name> --autostash
if remote and local branch is same, you can just do:
git pull --rebase --autostash
or if you are working on a "private branch" (a branch that you never pushed, but only merge or rebase on a public branch, one that you will push), you can set git global configuration with following two commands, these configurations will affect all "git pull" though:
git config --global pull.rebase true
git config --global rebase.autostash true
- This template also tracks monorepo version but skip releases for it. If pull request contains only monorepo version changes, manual operation is required to change pull request label from "autorelease: pendding" to "autorelease: tagged" after successful merge, otherwise you will get error, subsequent release-please will not run. Change it after CI is finished running, otherwise there will be loop.
If you don't want to track monorepo version, just remove the "." {...} block in release-please-config.json, then you don't need to do manual operation.
You can create new labels "autorelease: tagged not released" and "autorelease: published" for public clarification
- github requires single quote in workflow .yml files
ni @vuelib/core
- in src/main.js or src/main.ts:
import { createApp } from "vue";
import App from "./App.vue";
import VuePlugin from "@vuelib/core";
import "@vuelib/core/dist/style.css";
createApp(App).use(VuePlugin).mount("#app");
- create plugins folder, create a .js or .ts file, it will be automatically read and loaded by Nuxt:
import VuePlugin from '@vuelib/core'
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(VuePlugin)
})
-
unplugin-vue-components for components, UI framework components, etc.
- ni -D unplugin-vue-components
- in vite.config.js:
import Components from "unplugin-vue-components/vite"; export default defineConfig({ plugins: [ ... Components({ dts: resolve(__dirname, ".vitepress/components.d.ts"), include: [/\.vue$/, /\.vue\?vue/, /\.md$/], dirs: [ resolve(__dirname, ".vitepress/theme/components"), resolve(__dirname, "core/components"), ], }), ], })
- in tsconfig.json:
{ "include": ["components.d.ts"], }
-
unplugin-auto-import for Vue API (ref, computed, etc.), composables, etc.
- ni -D unplugin-auto-import
- in vite.config.js:
import AutoImport from "unplugin-auto-import/vite"; export default defineConfig({ plugins: [ ... AutoImport({ dts: true, include: [/\.[tj]sx?$/, /\.vue$/, /\.vue\?vue/], dirs: [resolve(__dirname, "use")], imports: ["vitest", "vitepress", "vue"], vueTemplate: true, }), ], })
- in tsconfig.json:
{ "include": ["auto-imports.d.ts"], }
- ni -D @vue/eslint-config-typescript eslint eslint-plugin-vue eslint-config-prettier eslint-plugin-prettier prettier typescript
- In root, create file ".eslintrc.js":
module.exports = {
root: true,
env: {
browser: true,
es2021: true,
node: true,
},
extends: [
"@vue/eslint-config-typescript",
"plugin:vue/vue3-recommended",
"plugin:prettier/recommended",
],
rules: {
"@typescript-eslint/no-unused-vars": "off",
},
};
- In root, create file ".eslintignore":
node_modules
.dist
.git
components.d.ts
auto-imports.d.ts
- In package.json, add commands:
"scripts": {
...
"lint": "eslint --ext .js,.ts,.vue --ignore-path .eslintignore .",
"lintfix": "nr lint --fix"
}
- In your favorite IDE, eg. vscode, install or enable prettier+eslint extension
Since TypeScript cannot handle type information for .vue
imports, they are shimmed to be a generic Vue component type by default. In most cases this is fine if you don't really care about component prop types outside of templates. However, if you wish to get actual prop types in .vue
imports (for example to get props validation when using manual h(...)
calls), you can enable Volar's Take Over mode by following these steps:
- Run
Extensions: Show Built-in Extensions
from VS Code's command palette, look forTypeScript and JavaScript Language Features
, then right click and selectDisable (Workspace)
. By default, Take Over mode will enable itself if the default TypeScript extension is disabled. - Reload the VS Code window by running
Developer: Reload Window
from the command palette.
You can learn more about Take Over mode here.
npx husky add .husky/post-merge 'pnpm install'
cd "package-root-folder"
pnpm add "dependency-package-name" --workspace
or
pnpm add "dependency-package-name" --filter "workspace-package-name"
<br />
at end of line- leave two spaces at end of Line
- 1 space
 
- 2 spaces
 
- 4 spaces
To fix typescript "Cannot find type definition file" for components error, you'll need standalone types/env.d.ts file
import AiButton from "./AiButton.vue" # Cannot find type definition file for "AiButton.vue" error
To fix vitest typescript "cannot find name" error, make sure your parent folders name doesn't include 2 symbols, ie. vue-components-lib will cause this error, but vue-componentslib will not cause this error
describe.concurrent("AiButton Test", async () => { ... } # Cannot find name "describe" error
-
Use gpg key If you encounter problems, check here How to debug gpg key info
-
gpg-agent, edit your ~/.gnupg/gpg-agent.conf, add these three lines:
default-cache-ttl 34560000 # 400 days
max-cache-ttl 34560000 # 400 days
then run
gpgconfig --reload gpgagent # restart gpg-agent
or
gpgconf --kill gpg-agent # kill gpg-agent
gpg-agent --daemon # start gpg-agent
- If you use an unattended machine, you can use gpg-preset-passphrase, but it still requires you to enter passphrase manually if gpg-agent is restarted, so it's not worth it, better off with above gpg-agent.conf option. I'll just put steps here for reference, run below script to cache your passphrase. You need to change $Your_Key_ID to your key id, which is last 8 characters of 2nd line when you display the whole key "gpg --list-secret-keys":
edit your ~/.gnupg/gpg-agent.conf
allow-preset-passphrase # allow use of gpg-preset-passphrase
#!/bin/bash
GPG_PRESET_PASS="/usr/lib/gnupg/gpg-preset-passphrase"
KEY_GRIP=$(gpg --with-keygrip --list-secret-keys $Your_Key_ID | grep -Pom1 '^ *Keygrip += +\K.*')
read -s -p "[gpg-preset-passphrase]: Enter passphrase to cache into gpg-agent: " PASSPHRASE; echo
$GPG_PRESET_PASS -c $KEY_GRIP <<< $PASSPHRASE
RETVAL=$?
if [ $RETVAL = 0 ]; then
echo "OK"
else
echo "NOT OK"
fi