Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .changeset/shared-runtime-imports.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
"@lynx-js/react": patch
---

feat: support declaring cross-thread shared modules via Import Attributes, enabling Main Thread Functions to call standard JS functions directly.

- Usage: Add `with { runtime: "shared" }` to the `import` statement. For example:

```ts
import { func } from './utils.js' with { runtime: 'shared' };

function worklet() {
'main thread';
func(); // callable inside a main thread function
}
```

- Limitations:
- Only directly imported identifiers are treated as shared; assigning the import to a new variable will result in the loss of this shared capability.
- Functions defined within shared modules do not automatically become Main Thread Functions. Accessing main-thread-only APIs (e.g., `MainThreadRef`) will cause errors.
117 changes: 117 additions & 0 deletions packages/react/transform/__test__/fixture.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1386,6 +1386,123 @@ class X extends Component {
});

describe('worklet', () => {
it('should error on unsupported runtime import attribute', async () => {
const result = await transformReactLynx(
`\
import { foo } from "./shared.js" with { runtime: "invalid" };
export function bar() {
"main thread";
foo();
}
`,
{
pluginName: '',
filename: '',
sourcemap: false,
cssScope: false,
jsx: false,
directiveDCE: true,
defineDCE: {
define: {
__LEPUS__: 'true',
__JS__: 'false',
},
},
shake: false,
compat: true,
refresh: false,
worklet: {
target: 'LEPUS',
filename: '',
runtimePkg: '@lynx-js/react',
},
},
);

expect(result.errors).toHaveLength(1);
expect(result.errors[0].text).toBe(
'Invalid runtime value. Only \'shared\' is supported.',
);
});

it('should error on non-string runtime import attribute', async () => {
const result = await transformReactLynx(
`\
import { foo } from "./shared.js" with { runtime: 123 };
export function bar() {
"main thread";
foo();
}
`,
{
pluginName: '',
filename: '',
sourcemap: false,
cssScope: false,
jsx: false,
directiveDCE: true,
defineDCE: {
define: {
__LEPUS__: 'true',
__JS__: 'false',
},
},
shake: false,
compat: true,
refresh: false,
worklet: {
target: 'LEPUS',
filename: '',
runtimePkg: '@lynx-js/react',
},
},
);

expect(result.errors).toHaveLength(1);
expect(result.errors[0].text).toBe(
'Invalid runtime value. Only \'shared\' is supported.',
);
});

it('should error on non-string \'runtime\' key runtime import attribute', async () => {
const result = await transformReactLynx(
`\
import { foo } from "./shared.js" with { 'runtime': 123 };
export function bar() {
"main thread";
foo();
}
`,
{
pluginName: '',
filename: '',
sourcemap: false,
cssScope: false,
jsx: false,
directiveDCE: true,
defineDCE: {
define: {
__LEPUS__: 'true',
__JS__: 'false',
},
},
shake: false,
compat: true,
refresh: false,
worklet: {
target: 'LEPUS',
filename: '',
runtimePkg: '@lynx-js/react',
},
},
);

expect(result.errors).toHaveLength(1);
expect(result.errors[0].text).toBe(
'Invalid runtime value. Only \'shared\' is supported.',
);
});

for (const target of ['LEPUS', 'JS', 'MIXED']) {
it('member expression', async () => {
const { code } = await transformReactLynx(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use swc_core::quote;

pub struct ExtractingIdentsCollectorConfig {
pub custom_global_ident_names: Option<Vec<String>>,
pub shared_identifiers: Option<FxHashSet<Id>>,
}

struct ScopeEnv {
Expand Down Expand Up @@ -252,6 +253,13 @@ impl VisitMut for ExtractingIdentsCollector {
}

fn visit_mut_ident(&mut self, n: &mut Ident) {
// Skip shared identifiers from shared-runtime imports
if let Some(ref shared_idents) = self.cfg.shared_identifiers {
if shared_idents.contains(&n.to_id()) {
return;
}
}

if !self
.scope_env
.iter()
Expand Down
Loading
Loading