From 218ee46b7d86a01b3344c05f4309af06406d2af1 Mon Sep 17 00:00:00 2001 From: "Timo K." Date: Fri, 4 Sep 2026 14:35:44 +0200 Subject: [PATCH] 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#&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 --- README.md | 17 +++++++++++++- component/package.json | 42 +++++++++++++++++++++++++++++++++++ component/pnpm-lock.yaml | 9 ++++++++ component/pnpm-workspace.yaml | 14 ++++++++++++ component/tsconfig.build.json | 20 +++++++++++++++++ package.json | 6 +++-- vite-component.config.ts | 4 ++++ 7 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 component/package.json create mode 100644 component/pnpm-lock.yaml create mode 100644 component/pnpm-workspace.yaml create mode 100644 component/tsconfig.build.json diff --git a/README.md b/README.md index 48b9bbc84..0732ff419 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/component/package.json b/component/package.json new file mode 100644 index 000000000..9ed81e02a --- /dev/null +++ b/component/package.json @@ -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#&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" + } +} diff --git a/component/pnpm-lock.yaml b/component/pnpm-lock.yaml new file mode 100644 index 000000000..490c2e4fa --- /dev/null +++ b/component/pnpm-lock.yaml @@ -0,0 +1,9 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: false + excludeLinksFromLockfile: false + +importers: + + .: {} diff --git a/component/pnpm-workspace.yaml b/component/pnpm-workspace.yaml new file mode 100644 index 000000000..d6c26ac66 --- /dev/null +++ b/component/pnpm-workspace.yaml @@ -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 diff --git a/component/tsconfig.build.json b/component/tsconfig.build.json new file mode 100644 index 000000000..18af65d84 --- /dev/null +++ b/component/tsconfig.build.json @@ -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": [] +} diff --git a/package.json b/package.json index 471cd28b2..fc5560931 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/vite-component.config.ts b/vite-component.config.ts index 127f599fa..ae0ac1508 100644 --- a/vite-component.config.ts +++ b/vite-component.config.ts @@ -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