Umd folder (#1704)

* Don't allow video autoplay to automatically unfreeze page. If it's a 'real' playback, there should be a mount or a keyboard event which will serve to unfreeze the page. Also add other non-user events to the list (we really should have an `isUserEvent` function)

* Apply formatting changes

* Create a new `umd` folder alongside `dist` for output of UMD files with a plain `.js` instead of `.cjs` extension, as the latter won't be served with the correct mime type by jsdelivr

 - #1687 (just rename `.cjs` to `.js`) was rejected due to the the 'dual package hazard' [1], and produces a warning when run through publint.dev (which was the original motivation for changing to \.cjs)
 - jsdelivr won't be serving `.cjs` with the correct mime type: https://github.com/jsdelivr/jsdelivr/issues/18584

[1] https://nodejs.org/en/learn/modules/publishing-a-package#the-dual-package-hazard

* Update to point to alpha.19 as presumably that's when the umd folder will be available after the changes in this PR

* Apply formatting changes

* Don't try to create the same directory twice (was failing on packages/packer/umd)

* Create thirty-shirts-grow.md

* Revert something that shouldn't have gotten into the UMD branch folder

* Apply formatting changes

* Update vite.config.default.ts

* Apply formatting changes

* build: include umd builds in published packages

Add umd directory to the files array in package.json for all packages to include UMD builds in npm publications. Also update .gitignore to exclude
umd folders from version control.

* Docs: point to correct file

* Remove unused code

* docs: update rrweb cdn urls to umd bundles

Align README and guide examples with published UMD file locations for
rrweb, @rrweb/record, and @rrweb/replay.

Update versioned rrweb script examples from 2.0.0-alpha.19 to
2.0.0-alpha.21 in both English and Chinese guides.

* build(all): include umd folder in package files

---------

Co-authored-by: eoghanmurray <eoghanmurray@users.noreply.github.com>
Co-authored-by: Justin Halsall <Juice10@users.noreply.github.com>
This commit is contained in:
Eoghan Murray
2026-02-13 14:03:23 +00:00
committed by GitHub
parent f0d25990c7
commit 33e01f5f00
25 changed files with 68 additions and 280 deletions

View File

@@ -0,0 +1,16 @@
---
"all": patch
"packer": patch
"plugins": patch
"record": patch
"replay": patch
"rrdom": patch
"rrdom-nodejs": patch
"rrweb": patch
"rrweb-player": patch
"rrweb-snapshot": patch
"types": patch
"utils": patch
---
Provide a /umd/ output folder alongside the /dist/ one so that we can serve UMD (Universal Module Definition) files with a .js extension, without upsetting expectations set by package.json that all .js files in /dist/ are modules

1
.gitignore vendored
View File

@@ -16,6 +16,7 @@ temp
# output of `yarn build`
build
dist
umd
# turbo cache
.turbo

View File

@@ -12,8 +12,8 @@
[![Join the chat at slack](https://img.shields.io/badge/slack-@rrweb-teal.svg?logo=slack)](https://join.slack.com/t/rrweb/shared_invite/zt-siwoc6hx-uWay3s2wyG8t5GpZVb8rWg)
[![Twitter Follow](https://img.shields.io/badge/twitter-@rrweb__io-teal.svg?logo=twitter)](https://twitter.com/rrweb_io)
[![Reddit](https://img.shields.io/badge/reddit-r/rrweb-teal.svg?logo=reddit)](https://www.reddit.com/r/rrweb)
![recorder gzip size](https://img.badgesize.io/https://cdn.jsdelivr.net/npm/@rrweb/record@latest/dist/record.min.js?compression=gzip&label=recorder%20gzip%20size&max=200000&softmax=100000)
![replayer gzip size](https://img.badgesize.io/https://cdn.jsdelivr.net/npm/@rrweb/replay@latest/dist/replay.min.js?compression=gzip&label=replayer%20gzip%20size&max=200000&softmax=100000)
![recorder gzip size](https://img.badgesize.io/https://cdn.jsdelivr.net/npm/@rrweb/record@latest/umd/record.min.js?compression=gzip&label=recorder%20gzip%20size&max=200000&softmax=100000)
![replayer gzip size](https://img.badgesize.io/https://cdn.jsdelivr.net/npm/@rrweb/replay@latest/umd/replay.min.js?compression=gzip&label=replayer%20gzip%20size&max=200000&softmax=100000)
[![](https://data.jsdelivr.com/v1/package/npm/rrweb/badge)](https://www.jsdelivr.com/package/npm/rrweb)
[中文文档](./README.zh_CN.md)

View File

@@ -11,8 +11,8 @@
[![Join the chat at slack](https://img.shields.io/badge/slack-@rrweb-teal.svg?logo=slack)](https://join.slack.com/t/rrweb/shared_invite/zt-siwoc6hx-uWay3s2wyG8t5GpZVb8rWg)
[![Reddit](https://img.shields.io/badge/reddit-r/rrweb-teal.svg?logo=reddit)](https://www.reddit.com/r/rrweb)
![total gzip size](https://img.badgesize.io/https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.cjs?compression=gzip&label=total%20gzip%20size)
![recorder gzip size](https://img.badgesize.io/https://cdn.jsdelivr.net/npm/rrweb@latest/dist/record/rrweb-record.min.cjs?compression=gzip&label=recorder%20gzip%20size)
![total gzip size](https://img.badgesize.io/https://cdn.jsdelivr.net/npm/rrweb@latest/umd/rrweb.min.js?compression=gzip&label=total%20gzip%20size)
![recorder gzip size](https://img.badgesize.io/https://cdn.jsdelivr.net/npm/@rrweb/record@latest/umd/record.min.js?compression=gzip&label=recorder%20gzip%20size)
[![](https://data.jsdelivr.com/v1/package/npm/rrweb/badge)](https://www.jsdelivr.com/package/npm/rrweb)
> 我已开通 Github Sponsor 您可以通过赞助的形式帮助 rrweb 的开发。

View File

@@ -15,13 +15,13 @@ You are recommended to install rrweb via jsdelivr's CDN service:
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/style.css"
/>
<script src="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.umd.min.cjs"></script>
<script src="https://cdn.jsdelivr.net/npm/rrweb@latest/umd/rrweb.min.js"></script>
```
Also, you can link to a specific version number that you can update manually:
```html
<script src="https://cdn.jsdelivr.net/npm/rrweb@2.0.0-alpha.14/dist/rrweb.umd.min.cjs"></script>
<script src="https://cdn.jsdelivr.net/npm/rrweb@2.0.0-alpha.21/umd/rrweb.min.js"></script>
```
#### Only include the recorder code
@@ -30,7 +30,7 @@ rrweb's code includes both the record and the replay parts. Most of the time you
This also can be done by using the `@rrweb/record` package and the CDN service:
```html
<script src="https://cdn.jsdelivr.net/npm/@rrweb/record@latest/dist/record.umd.min.cjs"></script>
<script src="https://cdn.jsdelivr.net/npm/@rrweb/record@latest/umd/record.min.js"></script>
```
The recorder UMD build exposes a global named `rrwebRecord`.
@@ -42,7 +42,7 @@ The recorder UMD build exposes a global named `rrwebRecord`.
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@rrweb/replay@latest/dist/style.css"
/>
<script src="https://cdn.jsdelivr.net/npm/@rrweb/replay@latest/dist/replay.umd.min.cjs"></script>
<script src="https://cdn.jsdelivr.net/npm/@rrweb/replay@latest/umd/replay.min.js"></script>
```
The replayer UMD build exposes a global named `rrwebReplay`.
@@ -340,7 +340,7 @@ rrweb-player can also be included with `<script>`
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/style.css"
/>
<script src="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/rrweb-player.umd.cjs"></script>
<script src="https://cdn.jsdelivr.net/npm/rrweb-player@latest/umd/rrweb-player.js"></script>
```
Or installed by using NPM

View File

@@ -13,13 +13,13 @@
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/style.css"
/>
<script src="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.umd.min.cjs"></script>
<script src="https://cdn.jsdelivr.net/npm/rrweb@latest/umd/rrweb.min.js"></script>
```
也可以在 URL 中指定具体的版本号,例如:
```html
<script src="https://cdn.jsdelivr.net/npm/rrweb@2.0.0-alpha.14/dist/rrweb.umd.min.cjs"></script>
<script src="https://cdn.jsdelivr.net/npm/rrweb@2.0.0-alpha.21/umd/rrweb.min.js"></script>
```
#### 仅引入录制部分
@@ -27,7 +27,7 @@
rrweb 代码分为录制和回放两部分,大多数时候用户在被录制的应用中只需要引入录制部分代码。同样可以通过使用 @rrweb/record 包和 CDN 服务来实现:
```html
<script src="https://cdn.jsdelivr.net/npm/@rrweb/record@latest/dist/record.umd.min.cjs"></script>
<script src="https://cdn.jsdelivr.net/npm/@rrweb/record@latest/umd/record.min.js"></script>
```
录制端的 UMD build 会暴露全局变量 `rrwebRecord`
@@ -39,7 +39,7 @@ rrweb 代码分为录制和回放两部分,大多数时候用户在被录制
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@rrweb/replay@latest/dist/style.css"
/>
<script src="https://cdn.jsdelivr.net/npm/@rrweb/replay@latest/dist/replay.umd.min.cjs"></script>
<script src="https://cdn.jsdelivr.net/npm/@rrweb/replay@latest/umd/replay.min.js"></script>
```
回放端的 UMD build 会暴露全局变量 `rrwebReplay`
@@ -334,7 +334,7 @@ rrweb-player 同样可以使用 CDN 方式安装:
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/style.css"
/>
<script src="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/rrweb-player.umd.cjs"></script>
<script src="https://cdn.jsdelivr.net/npm/rrweb-player@latest/umd/rrweb-player.js"></script>
```
或者通过 npm 安装:

View File

@@ -44,6 +44,7 @@
}
},
"files": [
"umd",
"build",
"dist",
"package.json"

View File

@@ -20,6 +20,7 @@
}
},
"files": [
"umd",
"dist",
"package.json"
],

View File

@@ -20,6 +20,7 @@
}
},
"files": [
"umd",
"dist",
"package.json"
],

View File

@@ -20,6 +20,7 @@
}
},
"files": [
"umd",
"dist",
"package.json"
],

View File

@@ -20,6 +20,7 @@
}
},
"files": [
"umd",
"dist",
"package.json"
],

View File

@@ -20,6 +20,7 @@
}
},
"files": [
"umd",
"dist",
"package.json"
],

View File

@@ -20,6 +20,7 @@
}
},
"files": [
"umd",
"dist",
"package.json"
],

View File

@@ -44,6 +44,7 @@
}
},
"files": [
"umd",
"dist",
"package.json"
],

View File

@@ -45,6 +45,7 @@
"./dist/style.css": "./dist/style.css"
},
"files": [
"umd",
"dist",
"package.json"
],

View File

@@ -33,6 +33,7 @@
}
},
"files": [
"umd",
"dist",
"package.json"
],

View File

@@ -21,6 +21,7 @@
}
},
"files": [
"umd",
"dist",
"package.json"
],

View File

@@ -19,7 +19,7 @@ rrweb-player can also be included with `<script>`
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/style.css"
/>
<script src="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/rrweb-player.umd.cjs"></script>
<script src="https://cdn.jsdelivr.net/npm/rrweb-player@latest/umd/rrweb-player.js"></script>
```
Or installed by using NPM

View File

@@ -51,6 +51,7 @@
"./dist/style.css": "./dist/style.css"
},
"files": [
"umd",
"dist",
"package.json"
],

View File

@@ -44,6 +44,7 @@
}
},
"files": [
"umd",
"dist",
"package.json"
],

View File

@@ -49,6 +49,7 @@
"./dist/style.css": "./dist/style.css"
},
"files": [
"umd",
"dist",
"package.json"
],

View File

@@ -1,261 +0,0 @@
import typescript from 'rollup-plugin-typescript2';
import esbuild from 'rollup-plugin-esbuild';
import resolve from '@rollup/plugin-node-resolve';
import postcss from 'rollup-plugin-postcss';
import renameNodeModules from 'rollup-plugin-rename-node-modules';
import webWorkerLoader from 'rollup-plugin-web-worker-loader';
import pkg from './package.json';
function toRecordPath(path) {
return path
.replace(/^([\w]+)\//, '$1/record/')
.replace('rrweb', 'rrweb-record');
}
function toRecordPackPath(path) {
return path
.replace(/^([\w]+)\//, '$1/record/')
.replace('rrweb', 'rrweb-record-pack');
}
function toReplayPath(path) {
return path
.replace(/^([\w]+)\//, '$1/replay/')
.replace('rrweb', 'rrweb-replay');
}
function toReplayUnpackPath(path) {
return path
.replace(/^([\w]+)\//, '$1/replay/')
.replace('rrweb', 'rrweb-replay-unpack');
}
function toAllPath(path) {
return path.replace('rrweb', 'rrweb-all');
}
function toPluginPath(pluginName, stage) {
return (path) =>
path
.replace(/^([\w]+)\//, '$1/plugins/')
.replace('rrweb', `${pluginName}-${stage}`);
}
function toMinPath(path) {
return path.replace(/\.js$/, '.min.js');
}
const baseConfigs = [
// all in one
{
input: './src/entries/all.ts',
name: 'rrweb',
pathFn: toAllPath,
esm: true,
},
// record only
{
input: './src/record/index.ts',
name: 'rrwebRecord',
pathFn: toRecordPath,
},
// record and pack
{
input: './src/entries/record-pack.ts',
name: 'rrwebRecord',
pathFn: toRecordPackPath,
},
// replay only
{
input: './src/replay/index.ts',
name: 'rrwebReplay',
pathFn: toReplayPath,
},
// replay and unpack
{
input: './src/entries/replay-unpack.ts',
name: 'rrwebReplay',
pathFn: toReplayUnpackPath,
},
// record and replay
{
input: './src/index.ts',
name: 'rrweb',
pathFn: (p) => p,
},
// plugins
{
input: './src/plugins/console/record/index.ts',
name: 'rrwebConsoleRecord',
pathFn: toPluginPath('console', 'record'),
},
{
input: './src/plugins/canvas-webrtc/record/index.ts',
name: 'rrwebCanvasWebRTCRecord',
pathFn: toPluginPath('canvas-webrtc', 'record'),
},
{
input: './src/plugins/canvas-webrtc/replay/index.ts',
name: 'rrwebCanvasWebRTCReplay',
pathFn: toPluginPath('canvas-webrtc', 'replay'),
},
{
input: './src/plugins/console/replay/index.ts',
name: 'rrwebConsoleReplay',
pathFn: toPluginPath('console', 'replay'),
},
{
input: './src/plugins/sequential-id/record/index.ts',
name: 'rrwebSequentialIdRecord',
pathFn: toPluginPath('sequential-id', 'record'),
},
{
input: './src/plugins/sequential-id/replay/index.ts',
name: 'rrwebSequentialIdReplay',
pathFn: toPluginPath('sequential-id', 'replay'),
},
];
let configs = [];
function getPlugins(options = {}) {
const { minify = false, sourceMap = false } = options;
return [
resolve({ browser: true }),
webWorkerLoader({
targetPlatform: 'browser',
inline: true,
preserveSource: true,
sourceMap,
}),
esbuild({
minify,
}),
postcss({
extract: true,
inject: false,
minimize: minify,
sourceMap,
}),
];
}
for (const c of baseConfigs) {
const basePlugins = [
resolve({ browser: true }),
// supports bundling `web-worker:..filename`
webWorkerLoader({
targetPlatform: 'browser',
inline: true,
preserveSource: true,
}),
typescript(),
];
const plugins = basePlugins.concat(
postcss({
extract: false,
inject: false,
}),
);
// browser
configs.push({
input: c.input,
plugins: getPlugins(),
output: [
{
name: c.name,
format: 'iife',
file: c.pathFn(pkg.unpkg),
},
],
});
// browser + minify
configs.push({
input: c.input,
plugins: getPlugins({ minify: true, sourceMap: true }),
output: [
{
name: c.name,
format: 'iife',
file: toMinPath(c.pathFn(pkg.unpkg)),
sourcemap: true,
},
],
});
// CommonJS
configs.push({
input: c.input,
plugins,
output: [
{
format: 'cjs',
file: c.pathFn('lib/rrweb.cjs'),
},
],
});
if (c.esm) {
// ES module
configs.push({
input: c.input,
plugins,
preserveModules: true,
output: [
{
format: 'esm',
dir: 'es/rrweb',
plugins: [renameNodeModules('ext')],
},
],
});
}
}
if (process.env.BROWSER_ONLY) {
const browserOnlyBaseConfigs = [
{
input: './src/index.ts',
name: 'rrweb',
pathFn: (p) => p,
},
{
input: './src/entries/all.ts',
name: 'rrweb',
pathFn: toAllPath,
},
{
input: './src/plugins/console/record/index.ts',
name: 'rrwebConsoleRecord',
pathFn: toPluginPath('console', 'record'),
},
{
input: './src/plugins/canvas-webrtc/record/index.ts',
name: 'rrwebCanvasWebRTCRecord',
pathFn: toPluginPath('canvas-webrtc', 'record'),
},
{
input: './src/plugins/canvas-webrtc/replay/index.ts',
name: 'rrwebCanvasWebRTCReplay',
pathFn: toPluginPath('canvas-webrtc', 'replay'),
},
];
configs = [];
for (const c of browserOnlyBaseConfigs) {
configs.push({
input: c.input,
plugins: getPlugins(),
output: [
{
name: c.name,
format: 'iife',
file: c.pathFn(pkg.unpkg),
},
],
});
}
}
export default configs;

View File

@@ -42,6 +42,7 @@
}
},
"files": [
"umd",
"dist",
"package.json"
],

View File

@@ -42,6 +42,7 @@
}
},
"files": [
"umd",
"dist",
"package.json"
],

View File

@@ -1,9 +1,9 @@
/// <reference types="vite/client" />
import dts from 'vite-plugin-dts';
import { copyFileSync } from 'node:fs';
import { copyFileSync, mkdirSync, existsSync } from 'node:fs';
import { defineConfig, LibraryOptions, LibraryFormats, Plugin } from 'vite';
import { build, Format } from 'esbuild';
import { resolve } from 'path';
import { resolve, dirname } from 'path';
import { umdWrapper } from 'esbuild-plugin-umd-wrapper';
import * as fs from 'node:fs';
import { visualizer } from 'rollup-plugin-visualizer';
@@ -51,22 +51,38 @@ function minifyAndUMDPlugin({
outDir,
});
} else {
const umdDir = dirname(outputFilePath).replace('/dist', '/umd');
if (!existsSync(umdDir)) {
mkdirSync(umdDir);
}
const outUmd = `${outputFilePath}.umd.cjs`;
await buildFile({
name,
input: inputFilePath,
output: `${outputFilePath}.umd.cjs`,
output: outUmd,
minify: false,
isCss: false,
outDir,
});
// Workaround because jsdelivr does use correct mime types for .umd.cjs
// More info: https://github.com/jsdelivr/jsdelivr/issues/18584 https://github.com/rrweb-io/rrweb/pull/1704
copyFileSync(
outUmd,
`${outputFilePath.replace('/dist/', '/umd/')}.js`,
);
const outUmdMin = `${outputFilePath}.umd.min.cjs`;
await buildFile({
name,
input: inputFilePath,
output: `${outputFilePath}.umd.min.cjs`,
output: outUmdMin,
minify: true,
isCss: false,
outDir,
});
copyFileSync(
outUmdMin,
`${outputFilePath.replace('/dist/', '/umd/')}.min.js`,
);
}
}
}