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 Element Call can also be embedded directly into another React application
rather than being loaded in an iframe as a widget. `pnpm build:component` 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 ```sh
pnpm dev:component 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, `component/build/scopeStylesToRoot.ts`). A host's own page keeps its styles,
and Element Call brings its own fonts and design tokens along. 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 ### Backend
A docker compose file `docker-compose-dev.yml` is provided to start the 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:development": "pnpm build:sdk --mode development",
"build:sdk": "pnpm build:full --config vite-sdk.config.js", "build:sdk": "pnpm build:full --config vite-sdk.config.js",
"build:sdk:production": "pnpm build:sdk", "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: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", "serve": "vite preview",
"format": "oxfmt", "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", "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 // confined to the element Element Call is mounted in
css: { postcss: { plugins: [scopeStylesToRoot()] } }, css: { postcss: { plugins: [scopeStylesToRoot()] } },
build: { 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", minify: mode === "production",
sourcemap: true, sourcemap: true,
// One stylesheet rather than one per chunk, so a host has a single file to // One stylesheet rather than one per chunk, so a host has a single file to