zelune 1.6.1 → 1.6.3
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/CHANGELOG.md +15 -0
- package/bin/cli.mjs +1 -2
- package/index.html +0 -6
- package/mcp/server.mjs +48 -7
- package/mcp/server.ts +1 -1
- package/package.json +1 -5
- package/plugin.json +1 -1
- package/plugins/viteReactNativeFallbackPlugin.ts +15 -12
- package/plugins/viteRnSourceInject.ts +4 -16
- package/src/canvas/FlowArrowsLayer.tsx +15 -12
- package/src/runtime/MockNavigationContext.tsx +252 -178
- package/src/runtime/reactNativeShim.ts +1558 -124
- package/src/server/apiMiddleware.ts +8 -5
- package/src/server/astEngine.ts +78 -7
- package/vite.config.ts +5 -6
|
@@ -237,8 +237,11 @@ export function viteCanvasApiMiddleware(workspaceRoot: string): Plugin {
|
|
|
237
237
|
|
|
238
238
|
const activeRoot = getActiveProjectRoot(workspaceRoot);
|
|
239
239
|
const layoutFilePath = path.resolve(activeRoot, '.canvas-layout.json');
|
|
240
|
-
const
|
|
241
|
-
const pathname =
|
|
240
|
+
const qIndex = req.url.indexOf('?');
|
|
241
|
+
const pathname = qIndex === -1 ? req.url : req.url.slice(0, qIndex);
|
|
242
|
+
const searchParams = new URLSearchParams(
|
|
243
|
+
qIndex === -1 ? '' : req.url.slice(qIndex + 1)
|
|
244
|
+
);
|
|
242
245
|
|
|
243
246
|
try {
|
|
244
247
|
// GET /api/screens
|
|
@@ -253,9 +256,9 @@ export function viteCanvasApiMiddleware(workspaceRoot: string): Plugin {
|
|
|
253
256
|
|
|
254
257
|
// GET /api/inspect?file=...&astPath=...&line=...
|
|
255
258
|
if (req.method === 'GET' && pathname === '/api/inspect') {
|
|
256
|
-
const file =
|
|
257
|
-
const astPath =
|
|
258
|
-
const lineParam =
|
|
259
|
+
const file = searchParams.get('file') ?? '';
|
|
260
|
+
const astPath = searchParams.get('astPath') ?? '';
|
|
261
|
+
const lineParam = searchParams.get('line');
|
|
259
262
|
const line = lineParam ? Number.parseInt(lineParam, 10) : undefined;
|
|
260
263
|
|
|
261
264
|
const element = await inspectElement(workspaceRoot, { file, astPath, line });
|
package/src/server/astEngine.ts
CHANGED
|
@@ -2,14 +2,88 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import fsSync from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { parse } from '@babel/parser';
|
|
5
|
-
import _traverse, { type NodePath } from '@babel/traverse';
|
|
6
5
|
import _generate from '@babel/generator';
|
|
7
6
|
import * as t from '@babel/types';
|
|
8
|
-
import { getJsxTagName } from '../../plugins/viteRnSourceInject';
|
|
9
7
|
|
|
10
|
-
const traverse = (_traverse as unknown as { default?: typeof _traverse }).default ?? _traverse;
|
|
11
8
|
const generate = (_generate as unknown as { default?: typeof _generate }).default ?? _generate;
|
|
12
9
|
|
|
10
|
+
export interface AstNodePath<T extends t.Node = t.Node> {
|
|
11
|
+
node: T;
|
|
12
|
+
parentPath: AstNodePath<t.Node> | null;
|
|
13
|
+
isJSXElement(): this is AstNodePath<t.JSXElement>;
|
|
14
|
+
isJSXFragment(): this is AstNodePath<t.JSXFragment>;
|
|
15
|
+
findParent(predicate: (p: AstNodePath<t.Node>) => boolean): AstNodePath<t.Node> | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type NodePath<T extends t.Node = t.Node> = AstNodePath<T>;
|
|
19
|
+
|
|
20
|
+
export type AstVisitorMap = {
|
|
21
|
+
[K in t.Node['type']]?: (path: AstNodePath<Extract<t.Node, { type: K }>>) => void;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export function getJsxTagName(nameNode: t.JSXOpeningElement['name']): string {
|
|
25
|
+
if (t.isJSXIdentifier(nameNode)) {
|
|
26
|
+
return nameNode.name;
|
|
27
|
+
}
|
|
28
|
+
if (t.isJSXMemberExpression(nameNode)) {
|
|
29
|
+
return `${getJsxTagName(nameNode.object)}.${nameNode.property.name}`;
|
|
30
|
+
}
|
|
31
|
+
if (t.isJSXNamespacedName(nameNode)) {
|
|
32
|
+
return `${nameNode.namespace.name}:${nameNode.name.name}`;
|
|
33
|
+
}
|
|
34
|
+
return 'Unknown';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function traverse(rootNode: t.Node, visitors: AstVisitorMap): void {
|
|
38
|
+
function createPath(node: t.Node, parentPath: AstNodePath<t.Node> | null): AstNodePath<t.Node> {
|
|
39
|
+
return {
|
|
40
|
+
node,
|
|
41
|
+
parentPath,
|
|
42
|
+
isJSXElement(): this is AstNodePath<t.JSXElement> {
|
|
43
|
+
return t.isJSXElement(node);
|
|
44
|
+
},
|
|
45
|
+
isJSXFragment(): this is AstNodePath<t.JSXFragment> {
|
|
46
|
+
return t.isJSXFragment(node);
|
|
47
|
+
},
|
|
48
|
+
findParent(predicate) {
|
|
49
|
+
let curr = parentPath;
|
|
50
|
+
while (curr) {
|
|
51
|
+
if (predicate(curr)) return curr;
|
|
52
|
+
curr = curr.parentPath;
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function visit(node: unknown, parentPath: AstNodePath<t.Node> | null): void {
|
|
60
|
+
if (!node || typeof node !== 'object' || typeof (node as t.Node).type !== 'string') return;
|
|
61
|
+
const typedNode = node as t.Node;
|
|
62
|
+
const pathObj = createPath(typedNode, parentPath);
|
|
63
|
+
const handler = visitors[typedNode.type as t.Node['type']] as
|
|
64
|
+
| ((p: AstNodePath<t.Node>) => void)
|
|
65
|
+
| undefined;
|
|
66
|
+
if (typeof handler === 'function') {
|
|
67
|
+
handler(pathObj);
|
|
68
|
+
}
|
|
69
|
+
const keys = t.VISITOR_KEYS[typedNode.type];
|
|
70
|
+
if (!keys) return;
|
|
71
|
+
const rec = typedNode as unknown as Record<string, unknown>;
|
|
72
|
+
for (const key of keys) {
|
|
73
|
+
const child = rec[key];
|
|
74
|
+
if (Array.isArray(child)) {
|
|
75
|
+
for (let i = 0; i < child.length; i++) {
|
|
76
|
+
if (child[i]) visit(child[i], pathObj);
|
|
77
|
+
}
|
|
78
|
+
} else if (child && typeof child === 'object') {
|
|
79
|
+
visit(child, pathObj);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
visit(rootNode, null);
|
|
85
|
+
}
|
|
86
|
+
|
|
13
87
|
export interface NavigationEdge {
|
|
14
88
|
id: string;
|
|
15
89
|
fromScreen: string;
|
|
@@ -807,10 +881,7 @@ function resolveCandidateToScreenId(
|
|
|
807
881
|
const trimmed = rawCandidate.trim();
|
|
808
882
|
if (!trimmed || trimmed.length > 80) return null;
|
|
809
883
|
if (
|
|
810
|
-
|
|
811
|
-
trimmed.startsWith('https://') ||
|
|
812
|
-
trimmed.startsWith('mailto:') ||
|
|
813
|
-
trimmed.startsWith('tel:') ||
|
|
884
|
+
/^[a-z][a-z0-9+.-]*:/i.test(trimmed) ||
|
|
814
885
|
trimmed.startsWith('#') ||
|
|
815
886
|
/\.(png|jpg|jpeg|svg|webp|gif|json|css)$/i.test(trimmed)
|
|
816
887
|
) {
|
package/vite.config.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { parse } from '@babel/parser';
|
|
2
2
|
import { build, defineConfig, loadEnv } from 'vite';
|
|
3
|
-
import react from '@vitejs/plugin-react';
|
|
4
3
|
import fs from 'node:fs';
|
|
5
4
|
import path from 'node:path';
|
|
6
5
|
import { fileURLToPath } from 'node:url';
|
|
@@ -22,6 +21,7 @@ const workspaceRoot = process.env.ANTIGRAVITY_WORKSPACE_ROOT
|
|
|
22
21
|
|
|
23
22
|
const targetProjectRoot = findMobileAppRoot(rawTargetRoot);
|
|
24
23
|
const mockNavShimPath = path.resolve(__dirname, 'src/runtime/MockNavigationContext.tsx');
|
|
24
|
+
const reactNativeShimPath = path.resolve(__dirname, 'src/runtime/reactNativeShim.ts');
|
|
25
25
|
|
|
26
26
|
function safeRealpath(targetPath: string): string {
|
|
27
27
|
try {
|
|
@@ -42,7 +42,6 @@ function resolvePackageRootDir(pkgName: string): string {
|
|
|
42
42
|
|
|
43
43
|
const resolvedReactDir = resolvePackageRootDir('react');
|
|
44
44
|
const resolvedReactDomDir = resolvePackageRootDir('react-dom');
|
|
45
|
-
const resolvedRnWebDir = resolvePackageRootDir('react-native-web');
|
|
46
45
|
const resolvedLucideDir = resolvePackageRootDir('lucide-react');
|
|
47
46
|
|
|
48
47
|
function loadPublicProjectEnv(roots: string[]): Record<string, string> {
|
|
@@ -288,7 +287,6 @@ export default defineConfig({
|
|
|
288
287
|
viteReactNativeFallbackPlugin(targetProjectRoot, mockNavShimPath, workspaceRoot),
|
|
289
288
|
viteVirtualScreensPlugin(targetProjectRoot),
|
|
290
289
|
viteRnSourceInject(targetProjectRoot),
|
|
291
|
-
react(),
|
|
292
290
|
viteCanvasApiMiddleware(targetProjectRoot),
|
|
293
291
|
],
|
|
294
292
|
optimizeDeps: {
|
|
@@ -299,22 +297,23 @@ export default defineConfig({
|
|
|
299
297
|
'react/jsx-dev-runtime',
|
|
300
298
|
'react-dom',
|
|
301
299
|
'react-dom/client',
|
|
302
|
-
'react-native-web',
|
|
303
300
|
'lucide-react',
|
|
304
301
|
],
|
|
305
302
|
},
|
|
306
303
|
esbuild: {
|
|
307
304
|
// Ensure .tsx/.ts/.jsx/.js files (including RN .js files with JSX) are transpiled cleanly
|
|
305
|
+
jsx: 'automatic',
|
|
308
306
|
include: /\.(tsx|ts|jsx|js)$/,
|
|
309
307
|
exclude: [],
|
|
310
308
|
loader: 'tsx',
|
|
311
309
|
},
|
|
312
310
|
resolve: {
|
|
313
|
-
dedupe: ['react', 'react-dom'
|
|
311
|
+
dedupe: ['react', 'react-dom'],
|
|
314
312
|
alias: {
|
|
315
313
|
react: resolvedReactDir,
|
|
316
314
|
'react-dom': resolvedReactDomDir,
|
|
317
|
-
'react-native
|
|
315
|
+
'react-native': reactNativeShimPath,
|
|
316
|
+
'react-native-web': reactNativeShimPath,
|
|
318
317
|
'lucide-react': resolvedLucideDir,
|
|
319
318
|
},
|
|
320
319
|
extensions: ['.web.tsx', '.web.ts', '.web.js', '.tsx', '.ts', '.jsx', '.js'],
|