react-native-webrtc-kaleidoscope 2.2.0 → 2.2.1

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/llms.txt CHANGED
@@ -81,7 +81,7 @@ Web (Chromium only) runs with no prebuild: `bunx expo start --web`.
81
81
  The plugin statically analyzes ONE file to decide which image assets ship in your native bundle. The contract:
82
82
 
83
83
  1. **Exact filename, exact location:** `kaleidoscope.preset-book.ts` at the **project root** (the directory containing `package.json` and your app config). A book named or placed differently still typechecks and still works on web, but at prebuild NO assets are copied and every image background silently renders nothing on native. This is the most common integration mistake; the failure is silent.
84
- 2. **The book is parsed as text, never executed.** Keep every `image` layer's `source` statically analyzable: a single named import from `react-native-webrtc-kaleidoscope/images/<category>/<leaf>`, a `require('./local.webp')` literal, or a `const X = Asset.fromModule(require('./local.webp')).uri` binding. Do not compute sources, build layers in helper functions, or re-export the book through a barrel; the parser will not follow them and the assets will be skipped (with at most a prebuild warning).
84
+ 2. **The book is parsed as text, never executed.** Keep every `image` layer's `source` statically analyzable: a single named import from `react-native-webrtc-kaleidoscope/images/<category>/<leaf>`, a `require('./local.webp')` literal, or a `const X = Asset.fromModule(require('./local.webp')).uri` binding (the binding may span lines; a formatter wrapping it at a narrow print width is fine). Do not compute sources, build layers in helper functions, or re-export the book through a barrel; the parser will not follow them, and each skipped `image` layer warns at prebuild naming the layer id.
85
85
  3. **An `image` layer's `id` doubles as its native plate id**: keep it equal to the image file's basename (`office-dark`, `my-bg`). Your own background images must be `.webp` (native resolves `assets/images/<id>.webp`).
86
86
  4. **Re-run `bunx expo prebuild` after editing the book** (adding/deleting presets changes the native asset set). JS-only uniform tweaks hot-reload without it.
87
87
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-webrtc-kaleidoscope",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "Live video effects (blur, background replacement, generative backgrounds, flip/rotate) for react-native-webrtc, packaged as a managed-Expo-friendly Expo Module. Working on web, Android, and iOS. Active development.",
5
5
  "keywords": [
6
6
  "react-native",
@@ -18,8 +18,11 @@
18
18
  // each `image` layer is `{ id, shader: 'image', source: <ref> }`, where <ref> is
19
19
  // a `require('./x.webp')` literal, a single named import from an
20
20
  // `.../images/<category>/<leaf>` specifier, or a `const X = ...require('./x.webp')...`
21
- // binding. Anything that can't be parsed or resolved warns (never throws),
22
- // matching the plugin's non-fatal contract.
21
+ // binding. A binding's initializer may span lines (a consumer's formatter wraps
22
+ // the idiomatic `Asset.fromModule(require(...)).uri` at narrow print widths), so
23
+ // the parse reads statements, not lines. Anything that can't be parsed or
24
+ // resolved warns (never throws), matching the plugin's non-fatal contract; an
25
+ // `image` layer dropped silently at prebuild is a bug, not a policy.
23
26
  var __importDefault =
24
27
  (this && this.__importDefault) ||
25
28
  function (mod) {
@@ -30,6 +33,7 @@ exports.PRESET_BOOK_FILENAME = void 0;
30
33
  exports.collectReferencedAssets = collectReferencedAssets;
31
34
  const node_fs_1 = __importDefault(require('node:fs'));
32
35
  const node_path_1 = __importDefault(require('node:path'));
36
+ const constants_1 = require('./constants');
33
37
  const file_manipulation_1 = require('./file-manipulation');
34
38
  /** The file a consumer declares at their project root. */
35
39
  exports.PRESET_BOOK_FILENAME = 'kaleidoscope.preset-book.ts';
@@ -48,8 +52,12 @@ function parseImports(source) {
48
52
  const specifier = m[3];
49
53
  if (local && specifier) imports[local] = specifier;
50
54
  }
55
+ // The initializer scan crosses newlines (formatters wrap the binding) but
56
+ // stops at `;` and refuses to run into a following declaration, so a
57
+ // no-semicolon style cannot misattribute a later require() to an earlier
58
+ // binding.
51
59
  const requireBindingRe =
52
- /(?:const|let|var)\s+([A-Za-z0-9_$]+)\s*=\s*[^;\n]*\brequire\(\s*['"]([^'"]+)['"]\s*\)/g;
60
+ /(?:const|let|var)\s+([A-Za-z0-9_$]+)\s*=\s*(?:(?!\b(?:const|let|var)\b)[^;])*?\brequire\(\s*['"]([^'"]+)['"]\s*\)/g;
53
61
  for (const m of source.matchAll(requireBindingRe)) {
54
62
  const local = m[1];
55
63
  const specifier = m[2];
@@ -81,14 +89,27 @@ function parseImageRefs(source) {
81
89
  for (const m of source.matchAll(layerRe)) {
82
90
  const body = m[1];
83
91
  if (!body) continue;
92
+ const layerId = body.match(/\bid\s*:\s*['"]([\w-]+)['"]/)?.[1];
84
93
  const sourceM = body.match(/source\s*:\s*(require\(\s*['"][^'"]+['"]\s*\)|[A-Za-z0-9_$]+)/);
85
94
  const expr = sourceM?.[1];
86
- if (!expr) continue;
95
+ // The warn-never-throw contract: a layer the parse cannot follow is skipped
96
+ // LOUDLY, naming the layer, so the missing plate surfaces at prebuild
97
+ // instead of on-device.
98
+ if (!expr) {
99
+ console.warn(
100
+ `${constants_1.LOG_TAG} Could not parse the 'source' of image layer '${layerId ?? '<no id>'}'; skipping its asset. An image layer's source must be a require('...') literal or an identifier bound by a single named import or a require() binding.`,
101
+ );
102
+ continue;
103
+ }
87
104
  const requireLiteral = expr.match(/require\(\s*['"]([^'"]+)['"]\s*\)/);
88
105
  const specifier = requireLiteral ? requireLiteral[1] : (imports[expr] ?? null);
89
- if (!specifier) continue;
90
- const idM = body.match(/\bid\s*:\s*['"]([\w-]+)['"]/);
91
- const id = idM?.[1] ?? imageIdFromSpecifier(specifier);
106
+ if (!specifier) {
107
+ console.warn(
108
+ `${constants_1.LOG_TAG} Could not resolve '${expr}' (the 'source' of image layer '${layerId ?? '<no id>'}') to an import or require() binding; skipping its asset.`,
109
+ );
110
+ continue;
111
+ }
112
+ const id = layerId ?? imageIdFromSpecifier(specifier);
92
113
  if (seen.has(id)) continue;
93
114
  seen.add(id);
94
115
  refs.push({ id, specifier });
@@ -96,13 +117,21 @@ function parseImageRefs(source) {
96
117
  return refs;
97
118
  }
98
119
  /**
99
- * Resolve an imported-composite specifier (`<pkg>/composites/<name>`) to its
100
- * source `.ts` on disk. Returns null for a non-composite specifier or an
101
- * unresolvable package.
120
+ * The composite a specifier references: `<pkg>/composites/<name>` or any of its
121
+ * per-composite subpaths (today `/controls`). A consumer importing ONLY a
122
+ * composite's controls is still using that composite, so both forms count.
123
+ */
124
+ function compositeNameFromSpecifier(specifier) {
125
+ return specifier.match(/(?:^|\/)composites\/([\w-]+)(?:\/controls)?$/)?.[1] ?? null;
126
+ }
127
+ /**
128
+ * Resolve an imported-composite specifier (`<pkg>/composites/<name>`, or its
129
+ * `/controls` subpath) to the composite's source `.ts` on disk. Returns null
130
+ * for a non-composite specifier or an unresolvable package.
102
131
  */
103
132
  function resolveCompositeSource(specifier, projectRoot) {
104
- if (!/(^|\/)composites\/[\w-]+$/.test(specifier)) return null;
105
- const name = specifier.substring(specifier.lastIndexOf('/') + 1);
133
+ const name = compositeNameFromSpecifier(specifier);
134
+ if (!name) return null;
106
135
  try {
107
136
  const pkgJson = require.resolve('react-native-webrtc-kaleidoscope/package.json', {
108
137
  paths: [projectRoot],
@@ -143,9 +172,13 @@ function resolveAssetPath(specifier, baseDir, projectRoot) {
143
172
  // collectors so neither drifts from the other.
144
173
  function walkBookSources(bookSource, projectRoot) {
145
174
  const sources = [{ source: bookSource, baseDir: projectRoot }];
175
+ // A composite and its `/controls` subpath resolve to the SAME source file;
176
+ // parse it once.
177
+ const seenPaths = new Set();
146
178
  for (const specifier of Object.values(parseImports(bookSource))) {
147
179
  const compositePath = resolveCompositeSource(specifier, projectRoot);
148
- if (!compositePath) continue;
180
+ if (!compositePath || seenPaths.has(compositePath)) continue;
181
+ seenPaths.add(compositePath);
149
182
  const compositeSource = (0, file_manipulation_1.readTextOrNull)(compositePath);
150
183
  // Non-fatal: a composite we cannot read contributes nothing.
151
184
  if (compositeSource !== null) {
@@ -182,10 +215,10 @@ function collectCompositeThumbRefs(bookSource, projectRoot) {
182
215
  const refs = [];
183
216
  const seen = new Set();
184
217
  for (const specifier of Object.values(parseImports(bookSource))) {
218
+ const name = compositeNameFromSpecifier(specifier);
219
+ if (!name || seen.has(name)) continue;
185
220
  const compositeTs = resolveCompositeSource(specifier, projectRoot);
186
221
  if (!compositeTs) continue;
187
- const name = specifier.substring(specifier.lastIndexOf('/') + 1);
188
- if (seen.has(name)) continue;
189
222
  const thumbPath = node_path_1.default.join(
190
223
  node_path_1.default.dirname(compositeTs),
191
224
  `${name}.thumb.webp`,