zelune 1.5.1 → 1.5.4
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 +30 -0
- package/README.md +8 -15
- package/bin/cli.mjs +17 -6
- package/index.html +9 -2
- package/package.json +2 -1
- package/plugin.json +1 -1
- package/plugins/viteReactNativeFallbackPlugin.ts +568 -0
- package/plugins/viteVirtualScreensPlugin.ts +139 -8
- package/src/canvas/CanvasApp.tsx +111 -23
- package/src/canvas/FlowArrowsLayer.tsx +5 -5
- package/src/canvas/InspectorPanel.tsx +58 -49
- package/src/canvas/ScreenArtboard.tsx +90 -29
- package/src/main.tsx +70 -1
- package/src/runtime/MockNavigationContext.tsx +1007 -38
- package/src/runtime/reactNativeShim.ts +141 -0
- package/src/screens/ProductDetailScreen.tsx +32 -59
- package/src/server/astEngine.ts +10 -1
- package/src/styles.css +182 -21
- package/vite.config.ts +136 -67
package/vite.config.ts
CHANGED
|
@@ -6,6 +6,7 @@ import path from 'node:path';
|
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { viteRnSourceInject } from './plugins/viteRnSourceInject';
|
|
8
8
|
import { viteVirtualScreensPlugin } from './plugins/viteVirtualScreensPlugin';
|
|
9
|
+
import { viteReactNativeFallbackPlugin } from './plugins/viteReactNativeFallbackPlugin';
|
|
9
10
|
import { viteCanvasApiMiddleware } from './src/server/apiMiddleware';
|
|
10
11
|
import { findMobileAppRoot } from './src/server/astEngine';
|
|
11
12
|
|
|
@@ -16,9 +17,21 @@ const rawTargetRoot = process.env.ANTIGRAVITY_TARGET_PROJECT
|
|
|
16
17
|
? path.resolve(process.env.ANTIGRAVITY_TARGET_PROJECT)
|
|
17
18
|
: __dirname;
|
|
18
19
|
|
|
20
|
+
const workspaceRoot = process.env.ANTIGRAVITY_WORKSPACE_ROOT
|
|
21
|
+
? path.resolve(process.env.ANTIGRAVITY_WORKSPACE_ROOT)
|
|
22
|
+
: rawTargetRoot;
|
|
23
|
+
|
|
19
24
|
const targetProjectRoot = findMobileAppRoot(rawTargetRoot);
|
|
20
25
|
const mockNavShimPath = path.resolve(__dirname, 'src/runtime/MockNavigationContext.tsx');
|
|
21
26
|
|
|
27
|
+
function safeRealpath(targetPath: string): string {
|
|
28
|
+
try {
|
|
29
|
+
return fs.realpathSync(targetPath);
|
|
30
|
+
} catch {
|
|
31
|
+
return path.resolve(targetPath);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
22
35
|
function resolvePackageRootDir(pkgName: string): string {
|
|
23
36
|
try {
|
|
24
37
|
const pkgJsonPath = requireFromZelune.resolve(`${pkgName}/package.json`);
|
|
@@ -33,98 +46,153 @@ const resolvedReactDomDir = resolvePackageRootDir('react-dom');
|
|
|
33
46
|
const resolvedRnWebDir = resolvePackageRootDir('react-native-web');
|
|
34
47
|
const resolvedLucideDir = resolvePackageRootDir('lucide-react');
|
|
35
48
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
'
|
|
46
|
-
|
|
47
|
-
'__proto__',
|
|
48
|
-
'constructor',
|
|
49
|
-
'prototype',
|
|
50
|
-
]);
|
|
49
|
+
/**
|
|
50
|
+
* Loads only safe client-public environment variables (EXPO_PUBLIC_*, NEXT_PUBLIC_*, VITE_*)
|
|
51
|
+
* from the target project's .env files so top-level SDK initializers in mobile screens work.
|
|
52
|
+
*/
|
|
53
|
+
function loadPublicProjectEnv(roots: string[]): Record<string, string> {
|
|
54
|
+
const publicEnv: Record<string, string> = {
|
|
55
|
+
NODE_ENV: 'development',
|
|
56
|
+
EXPO_PUBLIC_PLATFORM: 'web',
|
|
57
|
+
};
|
|
58
|
+
const envFiles = ['.env', '.env.development', '.env.local'];
|
|
59
|
+
const uniqueRoots = Array.from(new Set(roots.map((r) => path.resolve(r))));
|
|
51
60
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const cleanTarget = targets[0].replace(/\/\*$/, '');
|
|
80
|
-
const resolvedTarget = path.resolve(normalizedRoot, baseUrl, cleanTarget);
|
|
81
|
-
if (
|
|
82
|
-
resolvedTarget === normalizedRoot ||
|
|
83
|
-
resolvedTarget.startsWith(normalizedRoot + path.sep)
|
|
84
|
-
) {
|
|
85
|
-
aliases[cleanKey] = resolvedTarget;
|
|
61
|
+
for (const rootDir of uniqueRoots) {
|
|
62
|
+
for (const fileName of envFiles) {
|
|
63
|
+
const fullPath = path.join(rootDir, fileName);
|
|
64
|
+
try {
|
|
65
|
+
if (!fs.existsSync(fullPath)) continue;
|
|
66
|
+
const raw = fs.readFileSync(fullPath, 'utf-8');
|
|
67
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
68
|
+
const trimmed = line.trim();
|
|
69
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
70
|
+
const eqIdx = trimmed.indexOf('=');
|
|
71
|
+
if (eqIdx <= 0) continue;
|
|
72
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
73
|
+
if (
|
|
74
|
+
!key.startsWith('EXPO_PUBLIC_') &&
|
|
75
|
+
!key.startsWith('NEXT_PUBLIC_') &&
|
|
76
|
+
!key.startsWith('VITE_')
|
|
77
|
+
) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
let val = trimmed.slice(eqIdx + 1).trim();
|
|
81
|
+
if (
|
|
82
|
+
(val.startsWith('"') && val.endsWith('"')) ||
|
|
83
|
+
(val.startsWith("'") && val.endsWith("'"))
|
|
84
|
+
) {
|
|
85
|
+
val = val.slice(1, -1);
|
|
86
|
+
}
|
|
87
|
+
publicEnv[key] = val;
|
|
86
88
|
}
|
|
89
|
+
} catch {
|
|
90
|
+
// Ignore unreadable .env files
|
|
87
91
|
}
|
|
88
92
|
}
|
|
89
|
-
} catch {
|
|
90
|
-
// Ignore malformed tsconfig.json and fall back to standard @ alias below
|
|
91
93
|
}
|
|
94
|
+
return publicEnv;
|
|
95
|
+
}
|
|
92
96
|
|
|
93
|
-
|
|
94
|
-
const srcDir = path.join(normalizedRoot, 'src');
|
|
95
|
-
aliases['@'] = fs.existsSync(srcDir) ? srcDir : normalizedRoot;
|
|
96
|
-
}
|
|
97
|
+
const publicProjectEnv = loadPublicProjectEnv([workspaceRoot, rawTargetRoot, targetProjectRoot]);
|
|
97
98
|
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
const allowedFsRoots = Array.from(
|
|
100
|
+
new Set([
|
|
101
|
+
__dirname,
|
|
102
|
+
safeRealpath(__dirname),
|
|
103
|
+
workspaceRoot,
|
|
104
|
+
safeRealpath(workspaceRoot),
|
|
105
|
+
rawTargetRoot,
|
|
106
|
+
safeRealpath(rawTargetRoot),
|
|
107
|
+
targetProjectRoot,
|
|
108
|
+
safeRealpath(targetProjectRoot),
|
|
109
|
+
])
|
|
110
|
+
);
|
|
100
111
|
|
|
101
112
|
export default defineConfig({
|
|
102
113
|
plugins: [
|
|
114
|
+
{
|
|
115
|
+
name: 'vite-zelune-process-env-shim',
|
|
116
|
+
transformIndexHtml(html) {
|
|
117
|
+
const serializedEnv = JSON.stringify(publicProjectEnv);
|
|
118
|
+
const envScript = `<script>
|
|
119
|
+
(function() {
|
|
120
|
+
var baseEnv = ${serializedEnv};
|
|
121
|
+
var envProxy = new Proxy(baseEnv, {
|
|
122
|
+
get: function(target, prop) {
|
|
123
|
+
if (typeof prop !== 'string') return target[prop];
|
|
124
|
+
if (prop in target && target[prop] !== undefined && target[prop] !== '') {
|
|
125
|
+
return target[prop];
|
|
126
|
+
}
|
|
127
|
+
var upper = prop.toUpperCase();
|
|
128
|
+
if (upper.includes('URL') || upper.includes('ENDPOINT') || upper.includes('HOST')) {
|
|
129
|
+
return 'https://placeholder.example.com';
|
|
130
|
+
}
|
|
131
|
+
if (upper.includes('KEY') || upper.includes('TOKEN') || upper.includes('ID') || upper.includes('SECRET')) {
|
|
132
|
+
return 'placeholder-public-token';
|
|
133
|
+
}
|
|
134
|
+
return '';
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
function createRequireProxy() {
|
|
138
|
+
var fn = function() { return proxy; };
|
|
139
|
+
var proxy = new Proxy(fn, {
|
|
140
|
+
get: function(_t, prop) {
|
|
141
|
+
if (prop === Symbol.toPrimitive) return function(hint) { return hint === 'number' ? 0 : ''; };
|
|
142
|
+
if (prop === 'valueOf') return function() { return 0; };
|
|
143
|
+
if (prop === 'toString') return function() { return ''; };
|
|
144
|
+
if (prop === 'uri' || prop === 'default') return '';
|
|
145
|
+
return proxy;
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
return proxy;
|
|
149
|
+
}
|
|
150
|
+
window.global = window;
|
|
151
|
+
window.__DEV__ = true;
|
|
152
|
+
window.process = window.process || { env: envProxy, platform: 'browser', version: '', cwd: function() { return '/'; } };
|
|
153
|
+
window.process.env = envProxy;
|
|
154
|
+
window.require = window.require || function(modId) {
|
|
155
|
+
if (typeof modId === 'string' && /\\.(png|jpe?g|gif|webp|svg|avif|bmp)$/i.test(modId)) {
|
|
156
|
+
return { uri: modId, default: modId };
|
|
157
|
+
}
|
|
158
|
+
return createRequireProxy();
|
|
159
|
+
};
|
|
160
|
+
})();
|
|
161
|
+
</script>`;
|
|
162
|
+
return html.replace('<head>', `<head>\n ${envScript}`);
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
viteReactNativeFallbackPlugin(targetProjectRoot, mockNavShimPath, workspaceRoot),
|
|
103
166
|
viteVirtualScreensPlugin(targetProjectRoot),
|
|
104
167
|
viteRnSourceInject(targetProjectRoot),
|
|
105
168
|
react(),
|
|
106
169
|
viteCanvasApiMiddleware(targetProjectRoot),
|
|
107
170
|
],
|
|
171
|
+
optimizeDeps: {
|
|
172
|
+
noDiscovery: true,
|
|
173
|
+
include: [
|
|
174
|
+
'react',
|
|
175
|
+
'react/jsx-runtime',
|
|
176
|
+
'react/jsx-dev-runtime',
|
|
177
|
+
'react-dom',
|
|
178
|
+
'react-dom/client',
|
|
179
|
+
'react-native-web',
|
|
180
|
+
'lucide-react',
|
|
181
|
+
],
|
|
182
|
+
},
|
|
108
183
|
esbuild: {
|
|
109
|
-
// Ensure .tsx/.ts/.jsx files
|
|
110
|
-
include: /\.(tsx|ts|jsx)$/,
|
|
184
|
+
// Ensure .tsx/.ts/.jsx/.js files (including RN .js files with JSX) are transpiled cleanly
|
|
185
|
+
include: /\.(tsx|ts|jsx|js)$/,
|
|
111
186
|
exclude: [],
|
|
112
187
|
loader: 'tsx',
|
|
113
188
|
},
|
|
114
189
|
resolve: {
|
|
115
190
|
dedupe: ['react', 'react-dom', 'react-native-web'],
|
|
116
191
|
alias: {
|
|
117
|
-
...resolveTargetProjectPathAliases(targetProjectRoot),
|
|
118
192
|
react: resolvedReactDir,
|
|
119
193
|
'react-dom': resolvedReactDomDir,
|
|
120
|
-
'react-native': resolvedRnWebDir,
|
|
121
194
|
'react-native-web': resolvedRnWebDir,
|
|
122
195
|
'lucide-react': resolvedLucideDir,
|
|
123
|
-
'lucide-react-native': resolvedLucideDir,
|
|
124
|
-
'@react-navigation/native': mockNavShimPath,
|
|
125
|
-
'expo-router': mockNavShimPath,
|
|
126
|
-
'react-native-safe-area-context': mockNavShimPath,
|
|
127
|
-
'expo-status-bar': mockNavShimPath,
|
|
128
196
|
},
|
|
129
197
|
extensions: ['.web.tsx', '.web.ts', '.web.js', '.tsx', '.ts', '.jsx', '.js'],
|
|
130
198
|
},
|
|
@@ -142,7 +210,8 @@ export default defineConfig({
|
|
|
142
210
|
cors: false,
|
|
143
211
|
allowedHosts: ['127.0.0.1', 'localhost', '::1'],
|
|
144
212
|
fs: {
|
|
145
|
-
|
|
213
|
+
strict: false,
|
|
214
|
+
allow: allowedFsRoots,
|
|
146
215
|
},
|
|
147
216
|
},
|
|
148
217
|
});
|