embedded-react 0.3.0 → 0.4.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.
package/qjsc-wasm.mjs CHANGED
@@ -19,10 +19,10 @@
19
19
  // bytecode container with NO native toolchain. The module is built with `-sENVIRONMENT=web,node`, so the
20
20
  // same artifact that powers `dev` loads under Node here.
21
21
 
22
- import { createRequire } from 'node:module';
23
- import { dirname, resolve } from 'node:path';
24
- import { fileURLToPath } from 'node:url';
25
- import { existsSync } from 'node:fs';
22
+ import {createRequire} from 'node:module';
23
+ import {dirname, resolve} from 'node:path';
24
+ import {fileURLToPath} from 'node:url';
25
+ import {existsSync} from 'node:fs';
26
26
 
27
27
  const HERE = dirname(fileURLToPath(import.meta.url));
28
28
 
@@ -33,12 +33,17 @@ const HERE = dirname(fileURLToPath(import.meta.url));
33
33
  * @param {string} [simDir] Dir holding embedded-react.{js,wasm} (defaults to the package's sim/).
34
34
  * @returns {Promise<Buffer>} The bytecode bytes.
35
35
  */
36
- export async function compileToBytecode(jsSource, simDir = resolve(HERE, 'sim')) {
36
+ export async function compileToBytecode(
37
+ jsSource,
38
+ simDir = resolve(HERE, 'sim'),
39
+ ) {
37
40
  // The .cjs (not .js): this package is "type": "module", so Node would load the emscripten .js as ESM and
38
41
  // its CommonJS factory export would never run. The .cjs is the same module forced to CommonJS for Node.
39
42
  const loader = resolve(simDir, 'embedded-react.cjs');
40
43
  if (!existsSync(loader)) {
41
- throw new Error(`prebuilt simulator module not found at ${loader} (a published package ships it; from source run tools/web-sim/build.mjs)`);
44
+ throw new Error(
45
+ `prebuilt simulator module not found at ${loader} (a published package ships it; from source run tools/web-sim/build.mjs)`,
46
+ );
42
47
  }
43
48
  const require = createRequire(import.meta.url);
44
49
  const factory = require(loader); // forced CommonJS → MODULARIZE factory
@@ -53,7 +58,11 @@ export async function compileToBytecode(jsSource, simDir = resolve(HERE, 'sim'))
53
58
  Module.HEAPU8[srcPtr + bytes.length] = 0;
54
59
  const outLenPtr = Module._malloc(4);
55
60
 
56
- const compile = Module.cwrap('er_web_compile_bytecode', 'number', ['number', 'number', 'number']);
61
+ const compile = Module.cwrap('er_web_compile_bytecode', 'number', [
62
+ 'number',
63
+ 'number',
64
+ 'number',
65
+ ]);
57
66
  const bcPtr = compile(srcPtr, bytes.length, outLenPtr);
58
67
 
59
68
  // ALLOW_MEMORY_GROWTH=1 can detach views across the malloc/compile calls — read through a FRESH buffer.
@@ -64,7 +73,9 @@ export async function compileToBytecode(jsSource, simDir = resolve(HERE, 'sim'))
64
73
  Module._free(outLenPtr);
65
74
  if (!bcPtr || !bcLen) {
66
75
  if (bcPtr) Module._free(bcPtr);
67
- throw new Error('bytecode compile failed — check the bundle for a syntax error (see stderr above)');
76
+ throw new Error(
77
+ 'bytecode compile failed — check the bundle for a syntax error (see stderr above)',
78
+ );
68
79
  }
69
80
  const out = Buffer.from(heap().subarray(bcPtr, bcPtr + bcLen)); // copy out before freeing
70
81
  Module._free(bcPtr);
Binary file
package/sim-server.mjs CHANGED
@@ -23,18 +23,27 @@
23
23
  // Used by the consumer CLI (cli.mjs → `npx embedded-react dev` / `export`) and the repo dev loop
24
24
  // (tools/web-sim/dev.mjs). The only differences are paths, passed in here.
25
25
 
26
- import { createServer } from 'node:http';
27
- import { readFile } from 'node:fs/promises';
28
- import { existsSync, readFileSync, statSync } from 'node:fs';
29
- import { createRequire } from 'node:module';
30
- import { pathToFileURL } from 'node:url';
31
- import { basename, dirname, extname, relative, resolve } from 'node:path';
32
-
33
- const HERE = dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Za-z]:)/, '$1'));
26
+ import {createServer} from 'node:http';
27
+ import {readFile} from 'node:fs/promises';
28
+ import {existsSync, readFileSync, statSync} from 'node:fs';
29
+ import {createRequire} from 'node:module';
30
+ import {pathToFileURL} from 'node:url';
31
+ import {basename, dirname, extname, relative, resolve} from 'node:path';
32
+
33
+ const HERE = dirname(
34
+ new URL(import.meta.url).pathname.replace(/^\/([A-Za-z]:)/, '$1'),
35
+ );
34
36
  const require = createRequire(import.meta.url);
35
37
  const esbuild = require('esbuild');
36
- const { bakeAssetPack } = await import(pathToFileURL(resolve(HERE, 'assets/index.mjs')).href);
37
- const { transformPersist, shouldPersist } = await import(pathToFileURL(resolve(HERE, 'persist-transform.mjs')).href);
38
+ const {bakeAssetPack} = await import(
39
+ pathToFileURL(resolve(HERE, 'assets/index.mjs')).href
40
+ );
41
+ const {registerSvgVectorLoader} = await import(
42
+ pathToFileURL(resolve(HERE, 'assets/svg-loader.mjs')).href
43
+ );
44
+ const {transformPersist, shouldPersist} = await import(
45
+ pathToFileURL(resolve(HERE, 'persist-transform.mjs')).href
46
+ );
38
47
 
39
48
  const MIME = {
40
49
  '.html': 'text/html; charset=utf-8',
@@ -42,8 +51,8 @@ const MIME = {
42
51
  '.wasm': 'application/wasm',
43
52
  '.pack': 'application/octet-stream',
44
53
  };
45
- const assetName = (p) => basename(p).replace(/\.[^.]+$/, '');
46
- const mtime = (p) => {
54
+ const assetName = p => basename(p).replace(/\.[^.]+$/, '');
55
+ const mtime = p => {
47
56
  try {
48
57
  return statSync(p).mtimeMs;
49
58
  } catch {
@@ -62,7 +71,15 @@ const mtime = (p) => {
62
71
  * @param {() => void} [o.onRebuilt] Called after each successful build + asset bake (e.g. broadcast reload).
63
72
  * @returns {{ options: object, bundlePath: string, packPath: string }}
64
73
  */
65
- function createBundle({ entry, projectRoot, libSrc, nodePaths, outDir, persist = true, onRebuilt }) {
74
+ function createBundle({
75
+ entry,
76
+ projectRoot,
77
+ libSrc,
78
+ nodePaths,
79
+ outDir,
80
+ persist = true,
81
+ onRebuilt,
82
+ }) {
66
83
  const bundlePath = resolve(outDir, 'app.js');
67
84
  const packPath = resolve(outDir, 'assets.pack');
68
85
  const projNorm = projectRoot.replace(/\\/g, '/');
@@ -73,33 +90,62 @@ function createBundle({ entry, projectRoot, libSrc, nodePaths, outDir, persist =
73
90
  async function bakePack() {
74
91
  const discoveredSizes = [
75
92
  ...new Set(
76
- [...readFileSync(bundlePath, 'utf8').matchAll(/\bfontSize\s*:\s*(\d+(?:\.\d+)?)/g)].map((m) => Math.round(Number(m[1]))),
93
+ [
94
+ ...readFileSync(bundlePath, 'utf8').matchAll(
95
+ /\bfontSize\s*:\s*(\d+(?:\.\d+)?)/g,
96
+ ),
97
+ ].map(m => Math.round(Number(m[1]))),
77
98
  ),
78
99
  ].sort((a, b) => a - b);
79
100
 
80
101
  let cfg = {};
81
102
  const cp = resolve(projectRoot, 'assets.config.js');
82
- if (existsSync(cp)) cfg = (await import(`${pathToFileURL(cp).href}?t=${mtime(cp)}`)).default || {};
103
+ if (existsSync(cp))
104
+ cfg =
105
+ (await import(`${pathToFileURL(cp).href}?t=${mtime(cp)}`)).default ||
106
+ {};
83
107
  const fontConfig = cfg.fonts || {};
84
108
 
85
109
  const fontJobs = [...fonts.entries()].map(([family, path]) => {
86
110
  const fc = fontConfig[family] || {};
87
- const sizes = fc.sizes && fc.sizes.length ? fc.sizes : discoveredSizes.length ? discoveredSizes : [16];
88
- return { path, family, sizes, bpp: fc.bpp ?? 4, glyphs: fc.glyphs ?? 'ascii' };
111
+ const sizes =
112
+ fc.sizes && fc.sizes.length
113
+ ? fc.sizes
114
+ : discoveredSizes.length
115
+ ? discoveredSizes
116
+ : [16];
117
+ return {
118
+ path,
119
+ family,
120
+ sizes,
121
+ bpp: fc.bpp ?? 4,
122
+ glyphs: fc.glyphs ?? 'ascii',
123
+ };
89
124
  });
90
- const imageJobs = [...images.entries()].map(([name, path]) => ({ path, name }));
125
+ const imageJobs = [...images.entries()].map(([name, path]) => ({
126
+ path,
127
+ name,
128
+ }));
91
129
 
92
130
  const sig = JSON.stringify({
93
- i: imageJobs.map((j) => [j.name, j.path, mtime(j.path)]).sort(),
94
- f: fontJobs.map((j) => [j.family, j.path, mtime(j.path), j.sizes, j.bpp, j.glyphs]).sort(),
131
+ i: imageJobs.map(j => [j.name, j.path, mtime(j.path)]).sort(),
132
+ f: fontJobs
133
+ .map(j => [j.family, j.path, mtime(j.path), j.sizes, j.bpp, j.glyphs])
134
+ .sort(),
95
135
  });
96
136
  if (sig === lastAssetSig) return;
97
137
  lastAssetSig = sig;
98
138
  if (!imageJobs.length && !fontJobs.length) return; // built-in font only → no pack
99
139
 
100
140
  try {
101
- const s = bakeAssetPack({ images: imageJobs, fonts: fontJobs, outPath: packPath });
102
- console.log(` assets → ${s.images} image(s), ${s.fonts} font size(s), ${s.bytes} B`);
141
+ const s = bakeAssetPack({
142
+ images: imageJobs,
143
+ fonts: fontJobs,
144
+ outPath: packPath,
145
+ });
146
+ console.log(
147
+ ` assets → ${s.images} image(s), ${s.fonts} font size(s), ${s.bytes} B`,
148
+ );
103
149
  } catch (e) {
104
150
  console.error(` asset bake failed: ${e.message}`);
105
151
  }
@@ -113,9 +159,9 @@ function createBundle({ entry, projectRoot, libSrc, nodePaths, outDir, persist =
113
159
  platform: 'neutral',
114
160
  target: 'es2020',
115
161
  jsx: 'automatic',
116
- alias: { 'embedded-react': libSrc },
162
+ alias: {'embedded-react': libSrc},
117
163
  nodePaths,
118
- define: { 'process.env.NODE_ENV': '"production"' },
164
+ define: {'process.env.NODE_ENV': '"production"'},
119
165
  legalComments: 'none',
120
166
  logLevel: 'silent',
121
167
  plugins: [
@@ -126,25 +172,38 @@ function createBundle({ entry, projectRoot, libSrc, nodePaths, outDir, persist =
126
172
  images.clear();
127
173
  fonts.clear();
128
174
  });
129
- b.onLoad({ filter: /\.(jsx?|tsx?)$/ }, (a) => {
175
+ b.onLoad({filter: /\.(jsx?|tsx?)$/}, a => {
130
176
  // Transform ONLY the app's own source — never dependencies (see shouldPersist: excluding
131
177
  // node_modules is what stops the library's usePersistentState being rewritten to call itself).
132
178
  if (!persist || !shouldPersist(a.path, projNorm)) return undefined;
133
179
  try {
134
- return { contents: transformPersist(readFileSync(a.path, 'utf8'), relative(projectRoot, a.path).replace(/\\/g, '/')), loader: 'jsx' };
180
+ return {
181
+ contents: transformPersist(
182
+ readFileSync(a.path, 'utf8'),
183
+ relative(projectRoot, a.path).replace(/\\/g, '/'),
184
+ ),
185
+ loader: 'jsx',
186
+ };
135
187
  } catch (e) {
136
- return { errors: [{ text: `persist transform: ${e.message}` }] };
188
+ return {errors: [{text: `persist transform: ${e.message}`}]};
137
189
  }
138
190
  });
139
- b.onLoad({ filter: /\.(png|jpe?g|webp|gif|bmp)$/i }, (a) => {
191
+ b.onLoad({filter: /\.(png|jpe?g|webp|gif|bmp)$/i}, a => {
140
192
  images.set(assetName(a.path), a.path);
141
- return { contents: `module.exports = ${JSON.stringify(assetName(a.path))};`, loader: 'js' };
193
+ return {
194
+ contents: `module.exports = ${JSON.stringify(assetName(a.path))};`,
195
+ loader: 'js',
196
+ };
142
197
  });
143
- b.onLoad({ filter: /\.(ttf|otf)$/i }, (a) => {
198
+ b.onLoad({filter: /\.(ttf|otf)$/i}, a => {
144
199
  fonts.set(assetName(a.path), a.path);
145
- return { contents: `module.exports = ${JSON.stringify(assetName(a.path))};`, loader: 'js' };
200
+ return {
201
+ contents: `module.exports = ${JSON.stringify(assetName(a.path))};`,
202
+ loader: 'js',
203
+ };
146
204
  });
147
- b.onEnd(async (r) => {
205
+ registerSvgVectorLoader(b, (name, p) => images.set(name, p)); // raster-fallback SVGs join the image pack
206
+ b.onEnd(async r => {
148
207
  if (r.errors.length) {
149
208
  console.error(`✗ build failed (${r.errors.length} error(s))`);
150
209
  return;
@@ -157,15 +216,28 @@ function createBundle({ entry, projectRoot, libSrc, nodePaths, outDir, persist =
157
216
  ],
158
217
  };
159
218
 
160
- return { options, bundlePath, packPath };
219
+ return {options, bundlePath, packPath};
161
220
  }
162
221
 
163
222
  /**
164
223
  * One-shot bundle + asset bake into outDir (app.js [+ assets.pack]). For the static export.
165
224
  * Persist transform is off (a static page has no hot reload to preserve state across).
166
225
  */
167
- export async function buildApp({ entry, projectRoot, libSrc, nodePaths, outDir }) {
168
- const { options } = createBundle({ entry, projectRoot, libSrc, nodePaths, outDir, persist: false });
226
+ export async function buildApp({
227
+ entry,
228
+ projectRoot,
229
+ libSrc,
230
+ nodePaths,
231
+ outDir,
232
+ }) {
233
+ const {options} = createBundle({
234
+ entry,
235
+ projectRoot,
236
+ libSrc,
237
+ nodePaths,
238
+ outDir,
239
+ persist: false,
240
+ });
169
241
  await esbuild.build(options);
170
242
  }
171
243
 
@@ -177,16 +249,34 @@ export async function buildApp({ entry, projectRoot, libSrc, nodePaths, outDir }
177
249
  * @param {string[]} o.nodePaths
178
250
  * @param {number} o.port
179
251
  */
180
- export async function runDevServer({ entry, projectRoot, libSrc, nodePaths, indexHtml, simDir, outDir, port, label }) {
252
+ export async function runDevServer({
253
+ entry,
254
+ projectRoot,
255
+ libSrc,
256
+ nodePaths,
257
+ indexHtml,
258
+ simDir,
259
+ outDir,
260
+ port,
261
+ label,
262
+ }) {
181
263
  const clients = new Set();
182
264
  const broadcastReload = () => {
183
265
  for (const res of clients) res.write('data: reload\n\n');
184
266
  };
185
267
 
186
- const { options } = createBundle({ entry, projectRoot, libSrc, nodePaths, outDir, persist: true, onRebuilt: () => {
187
- console.log('↻ rebuilt → reloading');
188
- broadcastReload();
189
- } });
268
+ const {options} = createBundle({
269
+ entry,
270
+ projectRoot,
271
+ libSrc,
272
+ nodePaths,
273
+ outDir,
274
+ persist: true,
275
+ onRebuilt: () => {
276
+ console.log('↻ rebuilt → reloading');
277
+ broadcastReload();
278
+ },
279
+ });
190
280
 
191
281
  const ctx = await esbuild.context(options);
192
282
  await ctx.rebuild();
@@ -196,15 +286,26 @@ export async function runDevServer({ entry, projectRoot, libSrc, nodePaths, inde
196
286
  // outDir. The page references ./public/<name>. window.__erHot is injected so the page connects to /reload.
197
287
  const send = async (res, file) => {
198
288
  try {
199
- res.writeHead(200, { 'content-type': MIME[extname(file)] || 'application/octet-stream', 'cache-control': 'no-store' }).end(await readFile(file));
289
+ res
290
+ .writeHead(200, {
291
+ 'content-type': MIME[extname(file)] || 'application/octet-stream',
292
+ 'cache-control': 'no-store',
293
+ })
294
+ .end(await readFile(file));
200
295
  } catch {
201
296
  res.writeHead(404).end('not found');
202
297
  }
203
298
  };
204
299
  const server = createServer(async (req, res) => {
205
- const path = decodeURIComponent(new URL(req.url, 'http://localhost').pathname);
300
+ const path = decodeURIComponent(
301
+ new URL(req.url, 'http://localhost').pathname,
302
+ );
206
303
  if (path === '/reload') {
207
- res.writeHead(200, { 'content-type': 'text/event-stream', 'cache-control': 'no-cache', connection: 'keep-alive' });
304
+ res.writeHead(200, {
305
+ 'content-type': 'text/event-stream',
306
+ 'cache-control': 'no-cache',
307
+ connection: 'keep-alive',
308
+ });
208
309
  res.write('retry: 1000\n\n');
209
310
  clients.add(res);
210
311
  req.on('close', () => clients.delete(res));
@@ -212,8 +313,16 @@ export async function runDevServer({ entry, projectRoot, libSrc, nodePaths, inde
212
313
  }
213
314
  if (path === '/' || path === '/index.html') {
214
315
  try {
215
- const html = (await readFile(indexHtml, 'utf8')).replace('</head>', '<script>window.__erHot=true</script></head>');
216
- res.writeHead(200, { 'content-type': MIME['.html'], 'cache-control': 'no-store' }).end(html);
316
+ const html = (await readFile(indexHtml, 'utf8')).replace(
317
+ '</head>',
318
+ '<script>window.__erHot=true</script></head>',
319
+ );
320
+ res
321
+ .writeHead(200, {
322
+ 'content-type': MIME['.html'],
323
+ 'cache-control': 'no-store',
324
+ })
325
+ .end(html);
217
326
  } catch {
218
327
  res.writeHead(404).end('not found');
219
328
  }
@@ -221,13 +330,16 @@ export async function runDevServer({ entry, projectRoot, libSrc, nodePaths, inde
221
330
  }
222
331
  if (path.startsWith('/public/')) {
223
332
  const name = basename(path); // flat filename only
224
- const dir = name === 'embedded-react.js' || name === 'embedded-react.wasm' ? simDir : outDir;
333
+ const dir =
334
+ name === 'embedded-react.js' || name === 'embedded-react.wasm'
335
+ ? simDir
336
+ : outDir;
225
337
  return void send(res, resolve(dir, name));
226
338
  }
227
339
  res.writeHead(404).end('not found');
228
340
  });
229
341
 
230
- await new Promise((r) => server.listen(port, r));
342
+ await new Promise(r => server.listen(port, r));
231
343
  console.log(`embedded-react WASM sim → http://localhost:${port}/`);
232
344
  console.log(`watching ${label} — edit & save to hot-reload. Ctrl-C to quit.`);
233
345
 
@@ -240,5 +352,5 @@ export async function runDevServer({ entry, projectRoot, libSrc, nodePaths, inde
240
352
  };
241
353
  process.on('SIGINT', shutdown);
242
354
  process.on('SIGTERM', shutdown);
243
- return { ctx, server };
355
+ return {ctx, server};
244
356
  }
@@ -17,9 +17,9 @@
17
17
  // Animated — the React Native analog, backed by the engine's native-driver value system
18
18
  // (er_anim_value_*). An Animated.Value is a handle to an engine-side float; binding it to a node
19
19
  // prop (via Animated.View) lets the engine advance the animation each frame with NO per-frame JS.
20
- import { createElement, useRef, useEffect } from 'react';
21
- import { NativeUI } from '../native-ui.js';
22
- import { splitAnimatedStyle } from './split-style.js';
20
+ import {createElement, useRef, useEffect} from 'react';
21
+ import {NativeUI} from '../native-ui.js';
22
+ import {splitAnimatedStyle} from './split-style.js';
23
23
 
24
24
  /** A standalone animatable value bound to an engine-side float. */
25
25
  export class AnimatedValue {
@@ -74,7 +74,12 @@ export class AnimatedInterpolation {
74
74
  }
75
75
 
76
76
  __bind(node, prop) {
77
- NativeUI.animValueBindInterpolated(this._parent._handle, node, prop, this._config);
77
+ NativeUI.animValueBindInterpolated(
78
+ this._parent._handle,
79
+ node,
80
+ prop,
81
+ this._config,
82
+ );
78
83
  }
79
84
 
80
85
  interpolate(config) {
@@ -86,7 +91,7 @@ export class AnimatedInterpolation {
86
91
  /** Wraps fn so it runs at most once, regardless of how many times it is invoked. */
87
92
  function once(fn) {
88
93
  let called = false;
89
- return (arg) => {
94
+ return arg => {
90
95
  if (called) return;
91
96
  called = true;
92
97
  if (fn) fn(arg);
@@ -107,9 +112,9 @@ function makeAnimation(value, toValue, config) {
107
112
  _value: value,
108
113
  start(onComplete) {
109
114
  // Always pass a wrapper so we can null out the (possibly recycled) handle on completion.
110
- const cb = (finished) => {
115
+ const cb = finished => {
111
116
  handle = 0;
112
- if (onComplete) onComplete({ finished: !!finished });
117
+ if (onComplete) onComplete({finished: !!finished});
113
118
  };
114
119
  handle = NativeUI.animValueAnimate(value._handle, toValue, config, cb);
115
120
  },
@@ -120,19 +125,19 @@ function makeAnimation(value, toValue, config) {
120
125
  }
121
126
 
122
127
  export function timing(value, config = {}) {
123
- const { toValue = 0, useNativeDriver, ...rest } = config;
124
- return makeAnimation(value, toValue, { type: 'timing', ...rest });
128
+ const {toValue = 0, useNativeDriver, ...rest} = config;
129
+ return makeAnimation(value, toValue, {type: 'timing', ...rest});
125
130
  }
126
131
 
127
132
  export function spring(value, config = {}) {
128
- const { toValue = 0, useNativeDriver, ...rest } = config;
129
- return makeAnimation(value, toValue, { type: 'spring', ...rest });
133
+ const {toValue = 0, useNativeDriver, ...rest} = config;
134
+ return makeAnimation(value, toValue, {type: 'spring', ...rest});
130
135
  }
131
136
 
132
137
  export function decay(value, config = {}) {
133
- const { useNativeDriver, ...rest } = config;
138
+ const {useNativeDriver, ...rest} = config;
134
139
  // Decay coasts from its velocity; the engine ignores the target, so pass the current value.
135
- return makeAnimation(value, value.__getValue(), { type: 'decay', ...rest });
140
+ return makeAnimation(value, value.__getValue(), {type: 'decay', ...rest});
136
141
  }
137
142
 
138
143
  // --- Composition -------------------------------------------------------------------------------
@@ -152,22 +157,23 @@ export function sequence(animations) {
152
157
  current = 0;
153
158
  stopped = false;
154
159
  const done = once(onComplete);
155
- const next = (result) => {
160
+ const next = result => {
156
161
  if (stopped || !result || result.finished === false) {
157
- done({ finished: false });
162
+ done({finished: false});
158
163
  return;
159
164
  }
160
165
  if (current >= animations.length) {
161
- done({ finished: true });
166
+ done({finished: true});
162
167
  return;
163
168
  }
164
169
  animations[current++].start(next);
165
170
  };
166
- next({ finished: true }); // kick off the first entry
171
+ next({finished: true}); // kick off the first entry
167
172
  },
168
173
  stop() {
169
174
  stopped = true;
170
- if (current > 0 && current <= animations.length) animations[current - 1].stop();
175
+ if (current > 0 && current <= animations.length)
176
+ animations[current - 1].stop();
171
177
  },
172
178
  };
173
179
  }
@@ -181,12 +187,12 @@ export function parallel(animations, config) {
181
187
  const done = once(onComplete);
182
188
  const total = animations.length;
183
189
  if (total === 0) {
184
- done({ finished: true });
190
+ done({finished: true});
185
191
  return;
186
192
  }
187
193
  let doneCount = 0;
188
194
  let anyUnfinished = false;
189
- const onChild = (result) => {
195
+ const onChild = result => {
190
196
  doneCount++;
191
197
  if (!result || result.finished === false) {
192
198
  anyUnfinished = true;
@@ -195,7 +201,7 @@ export function parallel(animations, config) {
195
201
  for (const a of animations) a.stop();
196
202
  }
197
203
  }
198
- if (doneCount >= total) done({ finished: !anyUnfinished });
204
+ if (doneCount >= total) done({finished: !anyUnfinished});
199
205
  };
200
206
  for (const a of animations) a.start(onChild);
201
207
  },
@@ -215,15 +221,15 @@ export function stagger(delay, animations) {
215
221
  const done = once(onComplete);
216
222
  const total = animations.length;
217
223
  if (total === 0) {
218
- done({ finished: true });
224
+ done({finished: true});
219
225
  return;
220
226
  }
221
227
  let doneCount = 0;
222
228
  let anyUnfinished = false;
223
- const onChild = (result) => {
229
+ const onChild = result => {
224
230
  doneCount++;
225
231
  if (!result || result.finished === false) anyUnfinished = true;
226
- if (doneCount >= total) done({ finished: !anyUnfinished });
232
+ if (doneCount >= total) done({finished: !anyUnfinished});
227
233
  };
228
234
  animations.forEach((a, i) => {
229
235
  if (i === 0) {
@@ -253,7 +259,7 @@ export function delay(time) {
253
259
  const done = once(onComplete);
254
260
  timer = setTimeout(() => {
255
261
  timer = 0;
256
- done({ finished: true });
262
+ done({finished: true});
257
263
  }, time);
258
264
  },
259
265
  stop() {
@@ -271,33 +277,42 @@ export function delay(time) {
271
277
  * (resetBeforeIteration), matching RN — so a timing 0→1 repeats 0→1 rather than ping-ponging.
272
278
  */
273
279
  export function loop(animation, config) {
274
- const iterations = config && Number.isInteger(config.iterations) ? config.iterations : -1;
280
+ const iterations =
281
+ config && Number.isInteger(config.iterations) ? config.iterations : -1;
275
282
  const resetBeforeIteration = !config || config.resetBeforeIteration !== false;
276
283
  let stopped = false;
277
284
  return {
278
285
  _value: animation._value,
279
286
  start(onComplete) {
280
287
  const done = once(onComplete);
281
- const startValue = resetBeforeIteration && animation._value ? animation._value.__getValue() : null;
288
+ const startValue =
289
+ resetBeforeIteration && animation._value
290
+ ? animation._value.__getValue()
291
+ : null;
282
292
  let count = 0;
283
293
  const startIteration = () => {
284
294
  if (stopped) {
285
- done({ finished: false });
295
+ done({finished: false});
286
296
  return;
287
297
  }
288
298
  if (iterations >= 0 && count >= iterations) {
289
- done({ finished: true });
299
+ done({finished: true});
290
300
  return;
291
301
  }
292
302
  count++;
293
- if (resetBeforeIteration && animation._value && startValue != null && count > 1) {
303
+ if (
304
+ resetBeforeIteration &&
305
+ animation._value &&
306
+ startValue != null &&
307
+ count > 1
308
+ ) {
294
309
  animation._value.setValue(startValue);
295
310
  }
296
311
  animation.start(onIterationDone);
297
312
  };
298
- const onIterationDone = (result) => {
313
+ const onIterationDone = result => {
299
314
  if (stopped || !result || result.finished === false) {
300
- done({ finished: false });
315
+ done({finished: false});
301
316
  return;
302
317
  }
303
318
  // Defer the next iteration to a fresh task instead of starting it inline. A child animation can
@@ -343,13 +358,13 @@ export function useAnimatedValue(initial = 0) {
343
358
  */
344
359
  export function createAnimatedComponent(Component) {
345
360
  return function AnimatedComponent(props) {
346
- const { style, ...rest } = props;
347
- const { staticStyle, bindings } = splitAnimatedStyle(style);
348
- const ref = (node) => {
361
+ const {style, ...rest} = props;
362
+ const {staticStyle, bindings} = splitAnimatedStyle(style);
363
+ const ref = node => {
349
364
  if (node == null) return; // unmount
350
365
  for (const b of bindings) b.value.__bind(node, b.prop);
351
366
  };
352
- return createElement(Component, { ...rest, style: staticStyle, ref });
367
+ return createElement(Component, {...rest, style: staticStyle, ref});
353
368
  };
354
369
  }
355
370
 
@@ -21,8 +21,8 @@
21
21
  // "running the bundle IS starting the app", so registerComponent mounts immediately into a root
22
22
  // sized from the host-injected `screen` global. (A host-driven runApplication can be split out
23
23
  // later when the C host owns app lifecycle.)
24
- import { createElement } from 'react';
25
- import { createRoot } from '../renderer.js';
24
+ import {createElement} from 'react';
25
+ import {createRoot} from '../renderer.js';
26
26
 
27
27
  let registered = null;
28
28
 
@@ -32,7 +32,7 @@ export const AppRegistry = {
32
32
  * the component (RN signature), so the component module isn't evaluated until registration.
33
33
  */
34
34
  registerComponent(appKey, componentProvider) {
35
- registered = { appKey, Component: componentProvider() };
35
+ registered = {appKey, Component: componentProvider()};
36
36
  AppRegistry.runApplication(appKey);
37
37
  return appKey;
38
38
  },
@@ -43,7 +43,7 @@ export const AppRegistry = {
43
43
  runApplication(appKey) {
44
44
  if (!registered) return;
45
45
  if (appKey && appKey !== registered.appKey) return;
46
- const root = createRoot({ width: screen.width, height: screen.height });
46
+ const root = createRoot({width: screen.width, height: screen.height});
47
47
  root.render(createElement(registered.Component));
48
48
  },
49
49
  };
@@ -34,6 +34,6 @@ export const Easing = {
34
34
 
35
35
  /** Custom cubic-bezier curve via control points. */
36
36
  bezier(x1, y1, x2, y2) {
37
- return { x1, y1, x2, y2 };
37
+ return {x1, y1, x2, y2};
38
38
  },
39
39
  };