@symbiote-native/vue 3.5.0 → 3.6.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/build/composables/use-root-tag.d.ts +4 -0
- package/build/composables/use-root-tag.js +11 -0
- package/build/index.d.ts +2 -1
- package/build/index.js +2 -1
- package/build/render.js +5 -19
- package/metro-vue-transformer.cjs +75 -35
- package/package.json +4 -4
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// The tag of the surface a component is rendered into, the Vue twin of RN's `RootTagContext`:
|
|
2
|
+
// `mount` provides it on the app, a composable reads it back in `setup()`
|
|
3
|
+
import { inject } from '@vue/runtime-core';
|
|
4
|
+
export const ROOT_TAG_KEY = Symbol('symbiote-root-tag');
|
|
5
|
+
export function useRootTag() {
|
|
6
|
+
const rootTag = inject(ROOT_TAG_KEY, undefined);
|
|
7
|
+
if (rootTag === undefined) {
|
|
8
|
+
throw new Error('useRootTag() needs a component mounted by mount()');
|
|
9
|
+
}
|
|
10
|
+
return rootTag;
|
|
11
|
+
}
|
package/build/index.d.ts
CHANGED
|
@@ -60,12 +60,13 @@ export type { IComponentProvider, IAppConfig, IAppParameters, IRunnable, IHostRe
|
|
|
60
60
|
export { useColorScheme } from './composables/use-color-scheme';
|
|
61
61
|
export { useAnimatedColor, useAnimatedValue, useAnimatedValueXY, } from './composables/use-animated-value';
|
|
62
62
|
export { useWindowDimensions } from './composables/use-window-dimensions';
|
|
63
|
+
export { useRootTag } from './composables/use-root-tag';
|
|
63
64
|
export { createPermissionHook } from './composables/create-permission-hook';
|
|
64
65
|
export { createResourceHook } from './composables/create-resource-hook';
|
|
65
66
|
export { defineNativeViewComponent } from './composables/define-native-view-component';
|
|
66
67
|
export { createEventValueHook } from './composables/create-event-value-hook';
|
|
67
68
|
export { useEvent, useEventListener } from './composables/use-event';
|
|
68
|
-
export { Alert, Share, ActionSheetIOS, Linking, Vibration, ToastAndroid, Settings, I18nManager, Dimensions, Appearance, AppState, Keyboard, KEYBOARD_EVENT, TextInputState, BackHandler, PermissionsAndroid, PERMISSIONS, RESULTS, AccessibilityInfo, LayoutAnimation, } from '@symbiote-native/engine';
|
|
69
|
+
export { Alert, Share, ActionSheetIOS, Linking, Vibration, ToastAndroid, Settings, I18nManager, Dimensions, Appearance, matchMedia, MediaQueryList, MediaQueryListEvent, setVariable, getComputedVariable, registerProperty, AppState, Keyboard, KEYBOARD_EVENT, TextInputState, BackHandler, PermissionsAndroid, PERMISSIONS, RESULTS, AccessibilityInfo, LayoutAnimation, } from '@symbiote-native/engine';
|
|
69
70
|
export type { IAlertType, IAlertButtonStyle, IAlertButton, IAlertButtons, IAlertOptions, IShareContent, IShareOptions, IShareAction, IActionSheetIOSOptions, IShareActionSheetIOSOptions, IShareActionSheetError, IUrlEvent, II18nManagerConstants, IDisplayMetrics, IDisplayMetricsAndroid, IDimensionsPayload, IDimensionsSet, IDimensionsKey, IDimensionsChangeListener, IDimensionsStatic, IColorSchemeName, IColorSchemePreference, IAppStateStatus, IAppStateEvent, IKeyboardEventName, IKeyboardEvent, IKeyboardMetrics, IBackPressEventName, IBackPressHandler, IPermission, IPermissionStatus, IRationale, IAccessibilityChangeEvent, IAccessibilityChangeEventName, IAccessibilityChangeEventHandler, IAccessibilityAnnouncementFinishedEvent, IAnnounceForAccessibilityOptions, IAccessibilityEventType, ILayoutAnimationType, ILayoutAnimationProperty, ILayoutAnimationConfig, ILayoutAnimationAnim, } from '@symbiote-native/engine';
|
|
70
71
|
export { Platform, StyleSheet, processColor, setColorProcessor, dlog, isDebug, } from '@symbiote-native/engine';
|
|
71
72
|
export type { ISymbioteEvent, ISymbioteNode, IRootTag, } from '@symbiote-native/engine';
|
package/build/index.js
CHANGED
|
@@ -54,13 +54,14 @@ export { AppRegistry, HeadlessJsTaskError, setHostRegistrar, } from './modules/a
|
|
|
54
54
|
export { useColorScheme } from './composables/use-color-scheme.js';
|
|
55
55
|
export { useAnimatedColor, useAnimatedValue, useAnimatedValueXY, } from './composables/use-animated-value.js';
|
|
56
56
|
export { useWindowDimensions } from './composables/use-window-dimensions.js';
|
|
57
|
+
export { useRootTag } from './composables/use-root-tag.js';
|
|
57
58
|
export { createPermissionHook } from './composables/create-permission-hook.js';
|
|
58
59
|
export { createResourceHook } from './composables/create-resource-hook.js';
|
|
59
60
|
export { defineNativeViewComponent } from './composables/define-native-view-component.js';
|
|
60
61
|
export { createEventValueHook } from './composables/create-event-value-hook.js';
|
|
61
62
|
export { useEvent, useEventListener } from './composables/use-event.js';
|
|
62
63
|
// Imperative runtime modules: the SAME module both adapters share, re-exported from @symbiote-native/engine.
|
|
63
|
-
export { Alert, Share, ActionSheetIOS, Linking, Vibration, ToastAndroid, Settings, I18nManager, Dimensions, Appearance, AppState, Keyboard, KEYBOARD_EVENT, TextInputState, BackHandler, PermissionsAndroid, PERMISSIONS, RESULTS, AccessibilityInfo, LayoutAnimation, } from '@symbiote-native/engine';
|
|
64
|
+
export { Alert, Share, ActionSheetIOS, Linking, Vibration, ToastAndroid, Settings, I18nManager, Dimensions, Appearance, matchMedia, MediaQueryList, MediaQueryListEvent, setVariable, getComputedVariable, registerProperty, AppState, Keyboard, KEYBOARD_EVENT, TextInputState, BackHandler, PermissionsAndroid, PERMISSIONS, RESULTS, AccessibilityInfo, LayoutAnimation, } from '@symbiote-native/engine';
|
|
64
65
|
// Re-export the framework-agnostic engine surface (pure utilities + diagnostics).
|
|
65
66
|
export { Platform, StyleSheet, processColor, setColorProcessor, dlog, isDebug, } from '@symbiote-native/engine';
|
|
66
67
|
// Wired once by the app entry on a real host (like setColorProcessor): hands the engine
|
package/build/render.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// nativeFabricUIManager; RN's own renderer never in the path.
|
|
4
4
|
import { createSurface, disposeRoot, dlog, reportUncaughtError, } from '@symbiote-native/engine';
|
|
5
5
|
import { createSymbioteRenderer } from './renderer/index.js';
|
|
6
|
+
import { ROOT_TAG_KEY } from './composables/use-root-tag.js';
|
|
6
7
|
// One Vue app per surface, so a surface can be torn down (unmount) or cleanly
|
|
7
8
|
// re-mounted on the same rootTag: the bridgeless host stops and restarts a surface on
|
|
8
9
|
// Fast Refresh and on lifecycle/focus changes, reusing the rootTag.
|
|
@@ -34,25 +35,9 @@ function componentStack(instance) {
|
|
|
34
35
|
}
|
|
35
36
|
return frames.length === 0 ? null : frames.join('');
|
|
36
37
|
}
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
// the surface half-brought-up; in a release bundle it console.errors a bare error with no origin
|
|
41
|
-
// and no component context, never touching `global.ErrorUtils`, so nothing reaches the host.
|
|
42
|
-
// Setting a handler suppresses both branches and routes to the engine's shared channel instead —
|
|
43
|
-
// the native redbox on a device, console.error off one.
|
|
44
|
-
//
|
|
45
|
-
// CAUGHT vs UNCAUGHT — the asymmetry is Vue's own, and it costs us no code. `handleError` walks
|
|
46
|
-
// the `onErrorCaptured` chain BEFORE consulting `config.errorHandler`, and a hook returning
|
|
47
|
-
// `false` stops propagation and returns early (runtime-core 3.5 errorHandling.ts), so a boundary
|
|
48
|
-
// that claims the error never reaches this function at all. That is the behaviour we want and
|
|
49
|
-
// the same split the React adapter makes by hand: writing a boundary IS the developer saying
|
|
50
|
-
// "I am handling this", and answering it with a full-screen redbox over the fallback the app
|
|
51
|
-
// just rendered contradicts what the app asked for.
|
|
52
|
-
//
|
|
53
|
-
// So do NOT "fix" this to report from inside an `onErrorCaptured` of our own — that would
|
|
54
|
-
// re-report exactly the errors a boundary claimed. A hook that returns undefined instead of
|
|
55
|
-
// `false` still lands here, and correctly: Vue treats that error as still propagating.
|
|
38
|
+
// Without it Vue's `logError` re-throws from `app.mount()` in dev and hides the error in release
|
|
39
|
+
// A boundary that claims an error never gets here: `onErrorCaptured` runs before `errorHandler`
|
|
40
|
+
// Do not report from `onErrorCaptured`, it would re-report the errors a boundary claimed
|
|
56
41
|
function reportToHost(error, instance, info) {
|
|
57
42
|
// `info` is Vue's own phase label ('render function', 'setup function', 'native event
|
|
58
43
|
// handler'), which is the closest thing it has to React's "no error boundary" / "recovered"
|
|
@@ -82,6 +67,7 @@ export function mount(rootTag, rootComponent) {
|
|
|
82
67
|
const renderer = createSymbioteRenderer(surface);
|
|
83
68
|
const app = renderer.createApp(rootComponent);
|
|
84
69
|
app.config.errorHandler = reportToHost;
|
|
70
|
+
app.provide(ROOT_TAG_KEY, rootTag);
|
|
85
71
|
configurator?.(app);
|
|
86
72
|
apps.set(rootTag, app);
|
|
87
73
|
// The surface IS the Vue container: a top-level mutation routes to surface.appendChild
|
|
@@ -30,10 +30,13 @@ const {
|
|
|
30
30
|
compileCssModule,
|
|
31
31
|
compileCssToRules,
|
|
32
32
|
compileScopedCss,
|
|
33
|
+
conditionsArgument,
|
|
33
34
|
createCssMetroTransformer,
|
|
34
35
|
hashFilePath,
|
|
36
|
+
propertiesCall,
|
|
35
37
|
relabelForUpstream,
|
|
36
38
|
resolveUpstreamTransformer,
|
|
39
|
+
serializeRules,
|
|
37
40
|
} = require('@symbiote-native/css-parser');
|
|
38
41
|
|
|
39
42
|
const upstreamTransformer = resolveUpstreamTransformer();
|
|
@@ -191,23 +194,27 @@ const SFC_COMPONENT_VAR = '__sfc__';
|
|
|
191
194
|
|
|
192
195
|
// Each block restarts its rules at order 0, so a later one would tie an earlier one
|
|
193
196
|
// Renumbering on append gives the file one monotonic sequence, as one stylesheet would
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
+
// Conditions join the file's table, so a block's condition indexes shift by the ones already there
|
|
198
|
+
function appendRules(sink, compiled) {
|
|
199
|
+
const offset = sink.conditions.length;
|
|
200
|
+
sink.conditions.push(...compiled.conditions);
|
|
201
|
+
for (const rule of compiled.rules) {
|
|
202
|
+
sink.rules.push({
|
|
203
|
+
...withOffsetCond(rule, offset),
|
|
204
|
+
order: sink.rules.length,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
// A `:root` block cascades in append order, the way one stylesheet would
|
|
208
|
+
for (const variable of compiled.rootVariables) {
|
|
209
|
+
sink.rootVariables.push(withOffsetCond(variable, offset));
|
|
210
|
+
}
|
|
211
|
+
for (const property of compiled.properties) {
|
|
212
|
+
sink.properties.push(withOffsetCond(property, offset));
|
|
197
213
|
}
|
|
198
214
|
}
|
|
199
215
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
function serializeRules(rules) {
|
|
203
|
-
return JSON.stringify(
|
|
204
|
-
rules.map(({ tokens, specificity, order, style }) => ({
|
|
205
|
-
tokens,
|
|
206
|
-
specificity,
|
|
207
|
-
order,
|
|
208
|
-
style,
|
|
209
|
-
})),
|
|
210
|
-
);
|
|
216
|
+
function withOffsetCond(item, offset) {
|
|
217
|
+
return item.cond === undefined ? item : { ...item, cond: item.cond + offset };
|
|
211
218
|
}
|
|
212
219
|
|
|
213
220
|
// A style block's `lang` names a preprocessor (`lang="scss"`), where a file has an extension
|
|
@@ -276,7 +283,7 @@ function compileSfcStyleBlock({ style, content, filename, scopeId }) {
|
|
|
276
283
|
if (style.module) {
|
|
277
284
|
const compiled = compileCssModule(content, filename);
|
|
278
285
|
const bindingName = typeof style.module === 'string' ? style.module : '$style';
|
|
279
|
-
return {
|
|
286
|
+
return { ...compiled, binding: [bindingName, compiled.classMap] };
|
|
280
287
|
}
|
|
281
288
|
if (style.scoped) {
|
|
282
289
|
return compileScopedCss(content, {
|
|
@@ -284,7 +291,7 @@ function compileSfcStyleBlock({ style, content, filename, scopeId }) {
|
|
|
284
291
|
pattern: `[local]__${scopeId}`,
|
|
285
292
|
});
|
|
286
293
|
}
|
|
287
|
-
return
|
|
294
|
+
return compileCssToRules(content, { filename });
|
|
288
295
|
}
|
|
289
296
|
|
|
290
297
|
// Blocks cascade last-wins like CSS, carried by the rules' `order` that `appendRules` renumbers
|
|
@@ -294,9 +301,14 @@ async function compileStyles(descriptor, filename, scopeId) {
|
|
|
294
301
|
const contents = await Promise.all(
|
|
295
302
|
descriptor.styles.map(style => compileStyleBlockContent(style, filename)),
|
|
296
303
|
);
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
304
|
+
const sink = {
|
|
305
|
+
rules: [],
|
|
306
|
+
conditions: [],
|
|
307
|
+
rootVariables: [],
|
|
308
|
+
properties: [],
|
|
309
|
+
scopedClassNames: new Map(),
|
|
310
|
+
cssModuleBindings: new Map(),
|
|
311
|
+
};
|
|
300
312
|
descriptor.styles.forEach((style, index) => {
|
|
301
313
|
const block = compileSfcStyleBlock({
|
|
302
314
|
style,
|
|
@@ -304,18 +316,18 @@ async function compileStyles(descriptor, filename, scopeId) {
|
|
|
304
316
|
filename,
|
|
305
317
|
scopeId,
|
|
306
318
|
});
|
|
307
|
-
appendRules(
|
|
319
|
+
appendRules(sink, block);
|
|
308
320
|
for (const [name, renamed] of block.names ?? [])
|
|
309
|
-
scopedClassNames.set(name, renamed);
|
|
321
|
+
sink.scopedClassNames.set(name, renamed);
|
|
310
322
|
if (block.binding) {
|
|
311
323
|
const [bindingName, classMap] = block.binding;
|
|
312
|
-
cssModuleBindings.set(bindingName, {
|
|
313
|
-
...cssModuleBindings.get(bindingName),
|
|
324
|
+
sink.cssModuleBindings.set(bindingName, {
|
|
325
|
+
...sink.cssModuleBindings.get(bindingName),
|
|
314
326
|
...classMap,
|
|
315
327
|
});
|
|
316
328
|
}
|
|
317
329
|
});
|
|
318
|
-
return
|
|
330
|
+
return sink;
|
|
319
331
|
}
|
|
320
332
|
|
|
321
333
|
// Scoped-class rewriting is skipped when nothing in the file is scoped, so it adds no runtime cost
|
|
@@ -349,20 +361,37 @@ function compileComponentScript(descriptor, scopeId, styles) {
|
|
|
349
361
|
);
|
|
350
362
|
}
|
|
351
363
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
364
|
+
// The module text ahead of the component: the registration, the scope map and the module maps
|
|
365
|
+
// Only a scoped file imports the rename helper, so other files leave it out
|
|
366
|
+
function buildPreamble(styles) {
|
|
367
|
+
const {
|
|
368
|
+
rules,
|
|
369
|
+
conditions,
|
|
370
|
+
rootVariables,
|
|
371
|
+
properties,
|
|
372
|
+
scopedClassNames,
|
|
373
|
+
cssModuleBindings,
|
|
374
|
+
} = styles;
|
|
357
375
|
const isScoped = scopedClassNames.size > 0;
|
|
358
|
-
const
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
376
|
+
const hasRoot = rootVariables.length > 0;
|
|
377
|
+
const hasProperties = properties.length > 0;
|
|
378
|
+
const engineImports = [
|
|
379
|
+
'registerRules',
|
|
380
|
+
...(hasRoot ? ['registerRootVariables'] : []),
|
|
381
|
+
...(hasProperties ? ['registerProperties'] : []),
|
|
382
|
+
...(isScoped ? ['renameClassTokens as __scopeClass'] : []),
|
|
383
|
+
].join(', ');
|
|
384
|
+
const table = conditionsArgument(conditions);
|
|
362
385
|
const preamble = [
|
|
363
386
|
`import { ${engineImports} } from '@symbiote-native/engine';`,
|
|
364
|
-
`registerRules(${serializeRules(rules)});`,
|
|
387
|
+
`registerRules(${serializeRules(rules)}${table});`,
|
|
365
388
|
];
|
|
389
|
+
if (hasRoot) {
|
|
390
|
+
preamble.push(
|
|
391
|
+
`registerRootVariables(${JSON.stringify(rootVariables)}${table});`,
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
if (hasProperties) preamble.push(propertiesCall(properties, conditions));
|
|
366
395
|
if (isScoped) {
|
|
367
396
|
preamble.push(
|
|
368
397
|
`const __scopedClassNames = ${JSON.stringify(Object.fromEntries(scopedClassNames))};`,
|
|
@@ -373,8 +402,19 @@ function withStyleRegistration(code, { rules, scopedClassNames, cssModuleBinding
|
|
|
373
402
|
for (const [bindingName, classMap] of cssModuleBindings) {
|
|
374
403
|
preamble.push(`const ${bindingName} = ${JSON.stringify(classMap)};`);
|
|
375
404
|
}
|
|
405
|
+
return preamble.join('\n');
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function withStyleRegistration(code, styles) {
|
|
409
|
+
const { cssModuleBindings } = styles;
|
|
410
|
+
const hasCssModules = cssModuleBindings.size > 0;
|
|
411
|
+
const hasRegistrations =
|
|
412
|
+
styles.rules.length > 0 ||
|
|
413
|
+
styles.rootVariables.length > 0 ||
|
|
414
|
+
styles.properties.length > 0;
|
|
415
|
+
if (!hasRegistrations && !hasCssModules) return code;
|
|
376
416
|
|
|
377
|
-
const parts = [
|
|
417
|
+
const parts = [buildPreamble(styles), code];
|
|
378
418
|
if (hasCssModules) {
|
|
379
419
|
const bindings = [...cssModuleBindings.keys()]
|
|
380
420
|
.map(name => `${JSON.stringify(name)}: ${name}`)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbiote-native/vue",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.6.0",
|
|
4
4
|
"description": "SymbioteNative's Vue adapter — a @vue/runtime-core createRenderer driving real native iOS/Android views through the same engine as the React adapter.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -76,12 +76,12 @@
|
|
|
76
76
|
"peerDependencies": {
|
|
77
77
|
"vue": ">=3.5.0",
|
|
78
78
|
"react-native": ">=0.86",
|
|
79
|
-
"@symbiote-native/engine": "^1.
|
|
79
|
+
"@symbiote-native/engine": "^1.8.0"
|
|
80
80
|
},
|
|
81
81
|
"devDependencies": {
|
|
82
82
|
"@babel/core": "^7.25.2",
|
|
83
|
-
"@symbiote-native/engine": "1.
|
|
84
|
-
"@symbiote-native/test-utils": "0.5.
|
|
83
|
+
"@symbiote-native/engine": "1.8.0",
|
|
84
|
+
"@symbiote-native/test-utils": "0.5.1"
|
|
85
85
|
},
|
|
86
86
|
"scripts": {
|
|
87
87
|
"typecheck": "tsc --build",
|