Monorepo for open-source Kong UI components and utilities.
- What goes here
- Creating a package
- Package Publishing
- Development
- Moving packages to the public/private repo
- Host App Troubleshooting
Here are some criteria to help figure out if your code belongs to this mono-repository. If all of the following are true, you are welcome to create a new package:
- Your code is open-source and/or at a minimum available to the public, and UI-related. If you are writing the code to be used privately at Kong, then shared-ui-components is the better choice.
View the guide on creating a new package within the monorepo here
All packages must be created utilizing the pnpm run create-package
CLI. See here for more details.
Be sure to familiarize yourself with the Component Requirements, including style rules.
If you need to bump a version of a dependency for ALL consuming packages in this repo, use this command:
pnpm --recursive update {package-name}@{version}
Note that it will bump version of said dependency ONLY for those packages that have it as a dependency
or devDependency
, it won't add said dependency to package dependencies if it doesn't already have it.
- NodeJS >= 18.x
- pnpm 8.x (Why pnpm?). We recommend installing via the command shown here, substituting
8.6.12
with the version listed in thepackage.json > volta.pnpm
field. For example, for version8.6.12
you would use the commandcurl -fsSL https://get.pnpm.io/install.sh | env PNPM_VERSION=8.8.0 sh -
It is recommended to also globally install lerna
with pnpm
(though not absolutely required)
To get started, install dependencies
pnpm install
Next, make sure all dependent packages (from the monorepo itself) are built and available
pnpm run build
You can also run pnpm install-completion
to integrate pnpm
autocompletion into your command line tools.
Run the dev server in your packages/{workspace}/{package-name}/sandbox/
directory with hot-module reload
pnpm --filter "@kong-ui-public/{package-name}" run dev
Stylelint package files
# Lint only
pnpm --filter "@kong-ui-public/{package-name}" run stylelint
# Lint and fix
pnpm --filter "@kong-ui-public/{package-name}" run stylelint:fix
Lint package files
# Lint only
pnpm --filter "@kong-ui-public/{package-name}" run lint
# Lint and fix
pnpm --filter "@kong-ui-public/{package-name}" run lint:fix
Type check your package
pnpm --filter "@kong-ui-public/{package-name}" run typecheck
Run Component or Unit tests.
- Unit test files should be named
*.spec.ts
and will be run with Vitest - Component test files should be named
*.cy.ts
and will be run with Cypress component test runner.
# Component tests
pnpm --filter "@kong-ui-public/{package-name}" run test:component
# Component tests (with UI)
pnpm --filter "@kong-ui-public/{package-name}" run test:open
# Unit tests
pnpm --filter "@kong-ui-public/{package-name}" run test:unit
# Unit tests (with UI)
pnpm --filter "@kong-ui-public/{package-name}" run test:unit:open
Build your packages/{package-name}/sandbox/
directory for production and serve locally
pnpm --filter "@kong-ui-public/{package-name}" run build:sandbox
pnpm --filter "@kong-ui-public/{package-name}" run preview
pnpm --filter "@kong-ui-public/{package-name}" run build
This repo uses Conventional Commits. Commitizen and Commitlint are used to help build and enforce commit messages.
It is highly recommended to use the following command in order to create your commits:
pnpm commit
For more information on different components that compose our commit messages, please reference the Package Publishing docs
Lefthook is used to manage Git Hooks within the repo.
- A
commit-msg
hook is automatically setup that enforces commit message stands withcommitlint
, seelefthook.yml
- A
pre-push
hook is used that runsstylelint
andeslint
before allowing you to push your changes to the repository
Additionally, CI will use commitlint
to validate the commits associated with a PR in the Lint and Validate
job.
This only applies to TypeScript-only packages
If your package generates types, then add a build:docs
script to your package.json
file similar to the one in @kong-ui-public/analytics-utilities
"scripts": {
... other scripts
"build:docs": "{your command for generating docs}"
}
Please run the build:docs
command manually to generate the docs and then commit them to your PR.
You are working on the PR and changing component project. Let's say @kong-ui-public/i18n
. You want to try to deploy consuming application (khcp-ui
for example) that uses your changed code without merging your changes to main and publishing new version of @kong-ui-public/i18n
. Here are the steps:
- Look at your PR where your changes for
@kong-ui-public/i18n
. Every time PR is getting built, NPM preview of package is getting deployed, and there is an updated PR comment created:
Preview components from this PR in consuming application
In consuming application project install preview versions of shared packages generated by this PR:
// we are adding the version tagged on npm with your PR number
@kong-ui-public/i18n@pr-456
Install the preview version of the package in consuming application, let that PR be deployed, and see PR preview of consuming application utilizing @kong-ui-public/i18n
code from your shared-ui-components PR branch.
Never merge consuming application code that uses preview version of the package. PR versions will be deprecated and unpublished when your PR is closed.
You are developing shared component (let's say @kong-ui-public/forms
) and you need to run consuming application with the current version of the code you have locally in your public-ui-components/packages/{workspace}/forms
branch. Here is how to do it:
-
in the folder
public-ui-components/packages/{workspace}/forms
runpnpm link -g
-
make sure your package is getting build in watch mode, for this in the folder
public-ui-components/packages/{workspace}/forms
run:pnpm build:package --watch
-
In the root folder of the host application/package run:
pnpm link -g @kong-ui-public/forms
-
Run your consuming application as usual and enjoy your forms code changes visible in your local env immediately.
yarn run dev
In some cases HMR (hot module reloading) is not working out of the box in this configuration, to force it you might need following changes in vite.config.ts
of consuming application:
-
add
watch: ignored
into theserver
config:server: { watch: { ignored: ['!**/node_modules/@kong-ui-public/forms/**'] },
-
add
optimizeDeps
into the root of the config:optimizeDeps: { exclude: ['@kong-ui-public/forms'] },
Please do not commit these changes
And finally, when you done working with local (linked copy) of your
public-ui-components
package, unlink it: -
In the folder
public-ui-components/packages/{workspace}/forms
runpnpm remove -g @kong-ui-public/forms
-
In the root folder of the host application/package run:
pnpm unlink -g @kong-ui-public/forms pnpm install
Sometimes packages are initially created in Kong/shared-ui-components
where they are privately published, but need to be moved into the public Kong/public-ui-components
OSS repository so that the source code and npm package can be consumed by anyone.
View the docs on how to move packages between the private/public repositories here
Some ad blockers inadvertently block build files with the string analytics
in the name. As a proactive measure, the vite.config.ts
files in our packages utilize a sanitizePackageName
utility that replaces instances of the string analytics
with vitals
in generated filenames and global variables.
If your host application still has issues with ad blockers, you can try adding build
rules to your host application's vite.config.js
(or similar) to replace instances of the strings in components and packages:
// vite.config.ts
import { defineConfig } from 'vite'
// Replace any variation of string 'Analytics' in assets and chunks. Replacements are in order to preserve capitalization.
// The third replacement is a catch-all in case a string like `ANALYTICS` is present
const replaceAnalytics = (str: string) => str.replace(/Analytics/g, 'Vitals').replace(/analytics/gi, 'vitals')
export default defineConfig({
// ...
build: {
rollupOptions: {
output: {
chunkFileNames: (chunkInfo) => {
const name = replaceAnalytics(chunkInfo.name || '')
return `${name}.[hash].js`
},
assetFileNames: (assetInfo) => {
// Replace any instances of `analytics` in the external package name
const filename = replaceAnalytics(assetInfo.name || '').split('.')[0]
return `assets/${filename}.[hash].[ext]`
},
},
},
},
})