Make the component installable as a git dependency

`component/package.json` describes the built component as a package
(`@element-hq/element-call-component`: entry, stylesheet subpath, types,
peer dependencies) so that a host can depend on
`github:element-hq/element-call#<ref>&path:/component`. Its `prepare`
script builds on install, since nothing is published yet.

For that the component build now lands in `component/dist` instead of the
repository's `dist`, and `pnpm build:component` also emits the type
declarations (`component/tsconfig.build.json`, `build:component:types`),
which previously had to be produced by hand.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Timo K.
2026-09-04 15:29:55 +02:00
committed by Timo K.
co-authored by Claude Fable 5.1
parent 38c28a1c8f
commit 218ee46b7d
7 changed files with 109 additions and 3 deletions
+16 -1
View File
@@ -217,7 +217,8 @@ See also:
Element Call can also be embedded directly into another React application
rather than being loaded in an iframe as a widget. `pnpm build:component`
builds it as a library, and
builds it as a library into `component/dist` (the bundle, its stylesheet and
type declarations), and
```sh
pnpm dev:component
@@ -240,6 +241,20 @@ what is inside it, with `html`, `body` and `:root` standing for that root (see
`component/build/scopeStylesToRoot.ts`). A host's own page keeps its styles,
and Element Call brings its own fonts and design tokens along.
The package is not published yet. A host installs it as a git dependency on the
`component` directory of this repository,
```json
"@element-hq/element-call-component": "github:element-hq/element-call#main&path:/component"
```
whose `prepare` script runs the build on install (the host's pnpm has to allow
that: `allowBuilds` in its `pnpm-workspace.yaml`). It imports the component from
`@element-hq/element-call-component` and the stylesheet from
`@element-hq/element-call-component/style.css`, and has to provide `react`,
`react-dom`, `matrix-js-sdk` and `livekit-client` itself, since the bundle leaves
them external.
### Backend
A docker compose file `docker-compose-dev.yml` is provided to start the
+42
View File
@@ -0,0 +1,42 @@
{
"name": "@element-hq/element-call-component",
"version": "0.0.0",
"description": "Element Call as a React component. Consumed straight from the repository as a git dependency (github:element-hq/element-call#<ref>&path:/component): the host's package manager runs `prepare`, which builds `dist/`.",
"license": "SEE LICENSE IN ../README.md",
"repository": {
"type": "git",
"url": "https://github.com/element-hq/element-call",
"directory": "component"
},
"type": "module",
"devEngines": {
"packageManager": {
"name": "pnpm"
}
},
"files": [
"dist"
],
"main": "./dist/element-call.js",
"module": "./dist/element-call.js",
"types": "./dist/types/component/index.d.ts",
"exports": {
".": {
"types": "./dist/types/component/index.d.ts",
"default": "./dist/element-call.js"
},
"./style.css": "./dist/element-call.css"
},
"sideEffects": [
"*.css"
],
"scripts": {
"prepare": "cd .. && pnpm install --frozen-lockfile && pnpm build:component"
},
"peerDependencies": {
"livekit-client": "^2.18.1",
"matrix-js-sdk": "*",
"react": "^19",
"react-dom": "^19"
}
}
+9
View File
@@ -0,0 +1,9 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: false
excludeLinksFromLockfile: false
importers:
.: {}
+14
View File
@@ -0,0 +1,14 @@
# Makes `component/` a pnpm project of its own rather than a directory inside
# the repository's workspace. That matters when a host installs the component
# straight from this repository (`github:element-hq/element-call#…&path:/component`):
# pnpm prepares such a dependency by running `pnpm install` in this directory,
# and only a project root gets its `prepare` script (see package.json) run, which
# is what builds `dist/`. Inside the repository's own workspace the install
# would silently target the repository instead and build nothing.
#
# Consequence for development: pnpm commands run from within this directory see
# this project, not the repository; run them from the repository root.
# Nothing to install here: the peers in package.json are the host's, and the
# build runs against the repository's own node_modules (see `prepare`).
autoInstallPeers: false
+20
View File
@@ -0,0 +1,20 @@
{
// Declaration output for the component package (`pnpm build:component:types`).
// The root tsconfig only type-checks; this one emits `.d.ts` files, and nothing
// else, for everything the component's entry point reaches. The layout under
// `dist/types` mirrors the repository (`component/index.d.ts`, `src/…`), which
// is what `package.json` points its `types` at.
"extends": "../tsconfig.json",
"compilerOptions": {
"noEmit": false,
"emitDeclarationOnly": true,
"declaration": true,
"declarationMap": false,
"rootDir": "..",
"outDir": "./dist/types"
},
// The entry point, plus the ambient declarations (CSS modules, `?react` SVGs,
// `import.meta.env`, …) that the sources it reaches rely on.
"include": ["./index.tsx", "../src/@types/*.d.ts"],
"exclude": []
}
+4 -2
View File
@@ -17,9 +17,11 @@
"build:sdk:development": "pnpm build:sdk --mode development",
"build:sdk": "pnpm build:full --config vite-sdk.config.js",
"build:sdk:production": "pnpm build:sdk",
"build:component": "pnpm build:full --config vite-component.config.js",
"build:component": "pnpm build:component:js && pnpm build:component:types",
"build:component:js": "pnpm build:full --config vite-component.config.js",
"build:component:types": "tsc -p component/tsconfig.build.json",
"build:component:production": "pnpm build:component",
"build:component:development": "pnpm build:component --mode development",
"build:component:development": "pnpm build:component:js --mode development && pnpm build:component:types",
"serve": "vite preview",
"format": "oxfmt",
"format:check": "oxfmt --check; rc=$?; [[ $rc -ne 0 ]] && printf '\\033[46;30m INFO \\033[0m To fix, run: pnpm format\\n' >&2; exit $rc",
+4
View File
@@ -38,6 +38,10 @@ export default defineConfig(({ mode }) => {
// confined to the element Element Call is mounted in
css: { postcss: { plugins: [scopeStylesToRoot()] } },
build: {
// Into the package directory, so that `component/package.json` describes
// what sits next to it and the directory can be installed as a package
// (see its `files` and `exports`).
outDir: "component/dist",
minify: mode === "production",
sourcemap: true,
// One stylesheet rather than one per chunk, so a host has a single file to