@rendiv/bundler 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,10 @@
1
+ import { type InlineConfig } from 'vite';
2
+ export interface BundleOptions {
3
+ entryPoint: string;
4
+ outDir?: string;
5
+ publicDir?: string;
6
+ onProgress?: (progress: number) => void;
7
+ viteConfigOverride?: (config: InlineConfig) => InlineConfig;
8
+ }
9
+ export declare function bundle(options: BundleOptions): Promise<string>;
10
+ //# sourceMappingURL=bundle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,YAAY,EAAE,MAAM,MAAM,CAAC;AAOhD,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,kBAAkB,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,YAAY,CAAC;CAC7D;AAED,wBAAsB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAqFpE"}
package/dist/bundle.js ADDED
@@ -0,0 +1,75 @@
1
+ import { build } from 'vite';
2
+ import react from '@vitejs/plugin-react';
3
+ import path from 'node:path';
4
+ import os from 'node:os';
5
+ import fs from 'node:fs';
6
+ import { generateRenderEntryCode } from './render-entry-code.js';
7
+ export async function bundle(options) {
8
+ const { entryPoint, publicDir = 'public', onProgress, } = options;
9
+ const outDir = options.outDir ?? path.join(os.tmpdir(), `rendiv-bundle-${Date.now()}`);
10
+ const cwd = process.cwd();
11
+ const absoluteEntry = path.isAbsolute(entryPoint)
12
+ ? entryPoint
13
+ : path.resolve(cwd, entryPoint);
14
+ // Generate the render entry code
15
+ const renderEntryCode = generateRenderEntryCode(absoluteEntry);
16
+ // Write temp files in the project directory so Vite resolves modules correctly
17
+ const entryJsPath = path.join(cwd, '__rendiv_entry__.jsx');
18
+ const entryHtmlPath = path.join(cwd, '__rendiv_entry__.html');
19
+ fs.writeFileSync(entryJsPath, renderEntryCode);
20
+ fs.writeFileSync(entryHtmlPath, `<!DOCTYPE html>
21
+ <html>
22
+ <head>
23
+ <meta charset="UTF-8" />
24
+ <style>
25
+ * { margin: 0; padding: 0; box-sizing: border-box; }
26
+ html, body, #root { width: 100%; height: 100%; overflow: hidden; }
27
+ </style>
28
+ </head>
29
+ <body>
30
+ <div id="root"></div>
31
+ <script type="module" src="./__rendiv_entry__.jsx"></script>
32
+ </body>
33
+ </html>`);
34
+ onProgress?.(0.1);
35
+ try {
36
+ let viteConfig = {
37
+ root: cwd,
38
+ base: './',
39
+ publicDir: path.resolve(cwd, publicDir),
40
+ build: {
41
+ outDir,
42
+ emptyOutDir: true,
43
+ rollupOptions: {
44
+ input: entryHtmlPath,
45
+ },
46
+ minify: false,
47
+ sourcemap: false,
48
+ },
49
+ plugins: [react()],
50
+ logLevel: 'warn',
51
+ };
52
+ if (options.viteConfigOverride) {
53
+ viteConfig = options.viteConfigOverride(viteConfig);
54
+ }
55
+ onProgress?.(0.3);
56
+ await build(viteConfig);
57
+ onProgress?.(0.9);
58
+ // Rename the output HTML to index.html
59
+ const outputHtmlPath = path.join(outDir, '__rendiv_entry__.html');
60
+ const targetHtmlPath = path.join(outDir, 'index.html');
61
+ if (fs.existsSync(outputHtmlPath)) {
62
+ fs.renameSync(outputHtmlPath, targetHtmlPath);
63
+ }
64
+ onProgress?.(1.0);
65
+ }
66
+ finally {
67
+ // Clean up temp files
68
+ if (fs.existsSync(entryJsPath))
69
+ fs.unlinkSync(entryJsPath);
70
+ if (fs.existsSync(entryHtmlPath))
71
+ fs.unlinkSync(entryHtmlPath);
72
+ }
73
+ return outDir;
74
+ }
75
+ //# sourceMappingURL=bundle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle.js","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function generateHtmlTemplate(entryScript: string): string;
2
+ //# sourceMappingURL=html-template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-template.d.ts","sourceRoot":"","sources":["../src/html-template.ts"],"names":[],"mappings":"AAAA,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAehE"}
@@ -0,0 +1,17 @@
1
+ export function generateHtmlTemplate(entryScript) {
2
+ return `<!DOCTYPE html>
3
+ <html>
4
+ <head>
5
+ <meta charset="UTF-8" />
6
+ <style>
7
+ * { margin: 0; padding: 0; box-sizing: border-box; }
8
+ html, body, #root { width: 100%; height: 100%; overflow: hidden; }
9
+ </style>
10
+ </head>
11
+ <body>
12
+ <div id="root"></div>
13
+ <script type="module" src="${entryScript}"></script>
14
+ </body>
15
+ </html>`;
16
+ }
17
+ //# sourceMappingURL=html-template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-template.js","sourceRoot":"","sources":["../src/html-template.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,oBAAoB,CAAC,WAAmB;IACtD,OAAO;;;;;;;;;;;+BAWsB,WAAW;;QAElC,CAAC;AACT,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { bundle, type BundleOptions } from './bundle.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { bundle } from './bundle.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAsB,MAAM,aAAa,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Generates the JavaScript code for the render entry point.
3
+ * This code is injected into the bundle and bridges the headless browser
4
+ * with the Rendiv renderer via window globals.
5
+ */
6
+ export declare function generateRenderEntryCode(userEntryPoint: string): string;
7
+ //# sourceMappingURL=render-entry-code.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-entry-code.d.ts","sourceRoot":"","sources":["../src/render-entry-code.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAkKtE"}
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Generates the JavaScript code for the render entry point.
3
+ * This code is injected into the bundle and bridges the headless browser
4
+ * with the Rendiv renderer via window globals.
5
+ */
6
+ export function generateRenderEntryCode(userEntryPoint) {
7
+ // Use the absolute path directly for Vite to resolve
8
+ const importPath = userEntryPoint;
9
+ return `
10
+ import '${importPath}';
11
+ import { getRootComponent, CompositionManagerContext, TimelineContext, CompositionContext, RendivEnvironmentContext, getPendingHoldCount } from '@rendiv/core';
12
+ import React, { useState, useCallback, useMemo, useEffect } from 'react';
13
+ import { createRoot } from 'react-dom/client';
14
+
15
+ // Composition registry
16
+ const compositions = [];
17
+
18
+ function registerComposition(comp) {
19
+ const existing = compositions.findIndex(c => c.id === comp.id);
20
+ if (existing >= 0) compositions[existing] = comp;
21
+ else compositions.push(comp);
22
+ }
23
+
24
+ function unregisterComposition(id) {
25
+ const idx = compositions.findIndex(c => c.id === id);
26
+ if (idx >= 0) compositions.splice(idx, 1);
27
+ }
28
+
29
+ // Expose to renderer
30
+ window.__RENDIV_GET_COMPOSITIONS__ = () => {
31
+ return compositions.map(c => ({
32
+ id: c.id,
33
+ durationInFrames: c.durationInFrames,
34
+ fps: c.fps,
35
+ width: c.width,
36
+ height: c.height,
37
+ defaultProps: c.defaultProps,
38
+ type: c.type,
39
+ }));
40
+ };
41
+
42
+ window.__RENDIV_PENDING_HOLDS__ = () => getPendingHoldCount();
43
+
44
+ // State management
45
+ let currentFrame = 0;
46
+ let currentCompositionId = null;
47
+ let inputProps = {};
48
+ let rootInstance = null;
49
+ let resolveFrameReady = null;
50
+
51
+ window.__RENDIV_SET_FRAME__ = (frame) => {
52
+ currentFrame = frame;
53
+ rerender();
54
+ return new Promise((resolve) => {
55
+ resolveFrameReady = resolve;
56
+ // Check immediately if already ready
57
+ checkReady();
58
+ });
59
+ };
60
+
61
+ window.__RENDIV_SET_COMPOSITION__ = (id) => {
62
+ currentCompositionId = id;
63
+ rerender();
64
+ };
65
+
66
+ window.__RENDIV_SET_INPUT_PROPS__ = (props) => {
67
+ inputProps = props;
68
+ window.__RENDIV_INPUT_PROPS__ = props;
69
+ rerender();
70
+ };
71
+
72
+ function checkReady() {
73
+ if (resolveFrameReady && getPendingHoldCount() === 0) {
74
+ const resolve = resolveFrameReady;
75
+ resolveFrameReady = null;
76
+ // Use requestAnimationFrame to ensure React has flushed
77
+ requestAnimationFrame(() => {
78
+ requestAnimationFrame(() => {
79
+ resolve();
80
+ });
81
+ });
82
+ }
83
+ }
84
+
85
+ // Poll for pending holds
86
+ setInterval(checkReady, 16);
87
+
88
+ function App() {
89
+ const Root = getRootComponent();
90
+ if (!Root) return null;
91
+
92
+ const comp = compositions.find(c => c.id === currentCompositionId);
93
+
94
+ const managerValue = useMemo(() => ({
95
+ compositions,
96
+ registerComposition,
97
+ unregisterComposition,
98
+ currentCompositionId,
99
+ setCurrentCompositionId: (id) => { currentCompositionId = id; },
100
+ inputProps,
101
+ }), []);
102
+
103
+ // Registration phase: render Root to collect compositions
104
+ if (!currentCompositionId) {
105
+ return React.createElement(
106
+ CompositionManagerContext.Provider,
107
+ { value: managerValue },
108
+ React.createElement(Root)
109
+ );
110
+ }
111
+
112
+ // Rendering phase: render the selected composition
113
+ if (!comp) return null;
114
+
115
+ const Component = comp.component;
116
+ const mergedProps = { ...comp.defaultProps, ...inputProps };
117
+
118
+ const compositionConfig = {
119
+ id: comp.id,
120
+ width: comp.width,
121
+ height: comp.height,
122
+ fps: comp.fps,
123
+ durationInFrames: comp.durationInFrames,
124
+ defaultProps: mergedProps,
125
+ };
126
+
127
+ const timelineValue = {
128
+ frame: currentFrame,
129
+ playing: false,
130
+ playingRef: { current: false },
131
+ };
132
+
133
+ const envValue = { environment: 'rendering' };
134
+
135
+ return React.createElement(
136
+ CompositionManagerContext.Provider,
137
+ { value: managerValue },
138
+ React.createElement(
139
+ RendivEnvironmentContext.Provider,
140
+ { value: envValue },
141
+ React.createElement(
142
+ CompositionContext.Provider,
143
+ { value: compositionConfig },
144
+ React.createElement(
145
+ TimelineContext.Provider,
146
+ { value: timelineValue },
147
+ React.createElement(Root),
148
+ React.createElement(Component, mergedProps)
149
+ )
150
+ )
151
+ )
152
+ );
153
+ }
154
+
155
+ const container = document.getElementById('root');
156
+ rootInstance = createRoot(container);
157
+
158
+ function rerender() {
159
+ rootInstance.render(React.createElement(App));
160
+ }
161
+
162
+ rerender();
163
+
164
+ // Signal that the entry has loaded
165
+ window.__RENDIV_LOADED__ = true;
166
+ `;
167
+ }
168
+ //# sourceMappingURL=render-entry-code.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-entry-code.js","sourceRoot":"","sources":["../src/render-entry-code.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,cAAsB;IAC5D,qDAAqD;IACrD,MAAM,UAAU,GAAG,cAAc,CAAC;IAElC,OAAO;UACC,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4JnB,CAAC;AACF,CAAC"}
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@rendiv/bundler",
3
+ "version": "0.1.0",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "dependencies": {
18
+ "@vitejs/plugin-react": "^4.3.0",
19
+ "vite": "^6.0.0"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^22.0.0",
23
+ "typescript": "^5.7.0",
24
+ "@rendiv/tsconfig": "0.0.0"
25
+ },
26
+ "scripts": {
27
+ "build": "tsc",
28
+ "dev": "tsc --watch",
29
+ "typecheck": "tsc --noEmit",
30
+ "clean": "rm -rf dist"
31
+ }
32
+ }