zelune 1.6.3 → 1.6.5
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 +20 -0
- package/bin/cli.mjs +13 -53
- package/dist/assets/CheckoutScreen-CoLb16nl.js +229 -0
- package/dist/assets/HomeScreen-DtWSrKS1.js +316 -0
- package/dist/assets/LoginScreen-C1EtAYek.js +189 -0
- package/dist/assets/ProductDetailScreen-a9PXPTEY.js +196 -0
- package/dist/assets/ProfileScreen-ChKW9gJP.js +185 -0
- package/dist/assets/index-xam7XuSg.js +44764 -0
- package/{index.html → dist/index.html} +3 -1
- package/mcp/server.mjs +1906 -32
- package/package.json +10 -13
- package/plugin.json +1 -1
- package/socket.yml +11 -0
- package/mcp/server.ts +0 -446
- package/plugins/viteReactNativeFallbackPlugin.ts +0 -522
- package/plugins/viteRnSourceInject.ts +0 -105
- package/plugins/viteVirtualScreensPlugin.ts +0 -216
- package/src/canvas/CanvasApp.tsx +0 -1655
- package/src/canvas/FlowArrowsLayer.tsx +0 -239
- package/src/canvas/InspectorPanel.tsx +0 -1494
- package/src/canvas/ScreenArtboard.tsx +0 -1491
- package/src/canvas/types.ts +0 -23
- package/src/main.tsx +0 -80
- package/src/runtime/MockNavigationContext.tsx +0 -1869
- package/src/runtime/ScreenErrorBoundary.tsx +0 -86
- package/src/runtime/nativewindShim.ts +0 -997
- package/src/runtime/reactNativeShim.ts +0 -1674
- package/src/screens/CheckoutScreen.tsx +0 -229
- package/src/screens/HomeScreen.tsx +0 -288
- package/src/screens/LoginScreen.tsx +0 -176
- package/src/screens/ProductDetailScreen.tsx +0 -207
- package/src/screens/ProfileScreen.tsx +0 -172
- package/src/screens/index.ts +0 -1
- package/src/server/apiMiddleware.ts +0 -378
- package/src/server/astEngine.ts +0 -2624
- package/src/vite-env.d.ts +0 -7
- package/tsconfig.json +0 -23
- package/vite.config.ts +0 -339
- /package/{src/styles.css → dist/assets/index-C7nrS82N.css} +0 -0
package/mcp/server.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fs$1 from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import readline from "node:readline";
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
@@ -6,7 +6,8 @@ import fs from "node:fs/promises";
|
|
|
6
6
|
import { parse } from "@babel/parser";
|
|
7
7
|
import _generate from "@babel/generator";
|
|
8
8
|
import * as t from "@babel/types";
|
|
9
|
-
|
|
9
|
+
import http from "node:http";
|
|
10
|
+
const generate$1 = _generate.default ?? _generate;
|
|
10
11
|
function getJsxTagName(nameNode) {
|
|
11
12
|
if (t.isJSXIdentifier(nameNode)) {
|
|
12
13
|
return nameNode.name;
|
|
@@ -99,7 +100,7 @@ function countScreenFilesInDirectory(screensDir, maxDepth = 4) {
|
|
|
99
100
|
if (depth > maxDepth) return;
|
|
100
101
|
let entries = [];
|
|
101
102
|
try {
|
|
102
|
-
entries =
|
|
103
|
+
entries = fs$1.readdirSync(curr, { withFileTypes: true });
|
|
103
104
|
} catch {
|
|
104
105
|
return;
|
|
105
106
|
}
|
|
@@ -128,7 +129,7 @@ function hasDirectScreenFiles(dirPath) {
|
|
|
128
129
|
for (const rel of SCREEN_DIR_CANDIDATES) {
|
|
129
130
|
const full = path.join(dirPath, rel);
|
|
130
131
|
try {
|
|
131
|
-
if (
|
|
132
|
+
if (fs$1.existsSync(full) && fs$1.statSync(full).isDirectory()) {
|
|
132
133
|
const count = countScreenFilesInDirectory(full);
|
|
133
134
|
if (count > 0) {
|
|
134
135
|
return { found: true, screenCount: count };
|
|
@@ -145,13 +146,13 @@ function scoreMobileCandidate(dirPath, screenCount) {
|
|
|
145
146
|
if (/mobile|native|expo|app|client/.test(folderName)) {
|
|
146
147
|
score += 10;
|
|
147
148
|
}
|
|
148
|
-
if (
|
|
149
|
+
if (fs$1.existsSync(path.join(dirPath, "app.json")) || fs$1.existsSync(path.join(dirPath, "eas.json"))) {
|
|
149
150
|
score += 25;
|
|
150
151
|
}
|
|
151
152
|
try {
|
|
152
153
|
const pkgPath = path.join(dirPath, "package.json");
|
|
153
|
-
if (
|
|
154
|
-
const pkgRaw =
|
|
154
|
+
if (fs$1.existsSync(pkgPath)) {
|
|
155
|
+
const pkgRaw = fs$1.readFileSync(pkgPath, "utf-8");
|
|
155
156
|
if (pkgRaw.includes('"react-native"') || pkgRaw.includes('"expo"')) {
|
|
156
157
|
score += 20;
|
|
157
158
|
}
|
|
@@ -162,7 +163,7 @@ function scoreMobileCandidate(dirPath, screenCount) {
|
|
|
162
163
|
}
|
|
163
164
|
function findMobileAppRoot(rawRoot) {
|
|
164
165
|
const normalizedRoot = path.resolve(rawRoot);
|
|
165
|
-
if (!
|
|
166
|
+
if (!fs$1.existsSync(normalizedRoot)) {
|
|
166
167
|
return normalizedRoot;
|
|
167
168
|
}
|
|
168
169
|
const direct = hasDirectScreenFiles(normalizedRoot);
|
|
@@ -175,7 +176,7 @@ function findMobileAppRoot(rawRoot) {
|
|
|
175
176
|
if (depth > 2) return;
|
|
176
177
|
let entries = [];
|
|
177
178
|
try {
|
|
178
|
-
entries =
|
|
179
|
+
entries = fs$1.readdirSync(parentDir, { withFileTypes: true });
|
|
179
180
|
} catch {
|
|
180
181
|
return;
|
|
181
182
|
}
|
|
@@ -200,20 +201,20 @@ function findMobileAppRoot(rawRoot) {
|
|
|
200
201
|
}
|
|
201
202
|
function getActiveProjectRoot(fallbackRoot) {
|
|
202
203
|
const envTarget = process.env.ANTIGRAVITY_TARGET_PROJECT;
|
|
203
|
-
const baseRoot = envTarget &&
|
|
204
|
+
const baseRoot = envTarget && fs$1.existsSync(envTarget) ? path.resolve(envTarget) : path.resolve(fallbackRoot);
|
|
204
205
|
return findMobileAppRoot(baseRoot);
|
|
205
206
|
}
|
|
206
207
|
function resolveScreensDirectory(projectRoot) {
|
|
207
208
|
const normalizedRoot = findMobileAppRoot(projectRoot);
|
|
208
209
|
for (const rel of SCREEN_DIR_CANDIDATES) {
|
|
209
210
|
const full = path.join(normalizedRoot, rel);
|
|
210
|
-
if (
|
|
211
|
+
if (fs$1.existsSync(full) && fs$1.statSync(full).isDirectory() && countScreenFilesInDirectory(full) > 0) {
|
|
211
212
|
return { dirPath: full, relDir: rel };
|
|
212
213
|
}
|
|
213
214
|
}
|
|
214
215
|
for (const rel of SCREEN_DIR_CANDIDATES) {
|
|
215
216
|
const full = path.join(normalizedRoot, rel);
|
|
216
|
-
if (
|
|
217
|
+
if (fs$1.existsSync(full) && fs$1.statSync(full).isDirectory()) {
|
|
217
218
|
return { dirPath: full, relDir: rel };
|
|
218
219
|
}
|
|
219
220
|
}
|
|
@@ -223,20 +224,20 @@ function resolveScreensDirectory(projectRoot) {
|
|
|
223
224
|
};
|
|
224
225
|
}
|
|
225
226
|
const FORBIDDEN_OBJECT_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
226
|
-
function safeRealpath(targetPath) {
|
|
227
|
+
function safeRealpath$1(targetPath) {
|
|
227
228
|
try {
|
|
228
|
-
return
|
|
229
|
+
return fs$1.realpathSync(targetPath);
|
|
229
230
|
} catch {
|
|
230
231
|
return path.resolve(targetPath);
|
|
231
232
|
}
|
|
232
233
|
}
|
|
233
234
|
function resolveSafeScreenPath(workspaceRoot, relativeFile) {
|
|
234
|
-
const normalizedRoot = safeRealpath(getActiveProjectRoot(workspaceRoot));
|
|
235
|
+
const normalizedRoot = safeRealpath$1(getActiveProjectRoot(workspaceRoot));
|
|
235
236
|
const cleanRel = String(relativeFile ?? "").replace(/^[/\\]+/, "");
|
|
236
237
|
const resolved = path.resolve(normalizedRoot, cleanRel);
|
|
237
238
|
const allowedDirs = ["src/screens", "screens", "app", "src/app"].map((d) => {
|
|
238
239
|
const candidate = path.resolve(normalizedRoot, d);
|
|
239
|
-
return
|
|
240
|
+
return fs$1.existsSync(candidate) ? safeRealpath$1(candidate) : candidate;
|
|
240
241
|
});
|
|
241
242
|
const isLexicallyAllowed = allowedDirs.some(
|
|
242
243
|
(dir) => resolved.startsWith(dir + path.sep) || resolved === dir
|
|
@@ -249,14 +250,14 @@ function resolveSafeScreenPath(workspaceRoot, relativeFile) {
|
|
|
249
250
|
if (!resolved.endsWith(".tsx") && !resolved.endsWith(".jsx")) {
|
|
250
251
|
throw new Error(`Invalid file extension for "${relativeFile}". Only .tsx and .jsx are allowed.`);
|
|
251
252
|
}
|
|
252
|
-
if (
|
|
253
|
-
const stat =
|
|
253
|
+
if (fs$1.existsSync(resolved)) {
|
|
254
|
+
const stat = fs$1.lstatSync(resolved);
|
|
254
255
|
if (stat.isSymbolicLink()) {
|
|
255
256
|
throw new Error(
|
|
256
257
|
`Security violation: Symbolic link "${relativeFile}" is not permitted inside screen directories`
|
|
257
258
|
);
|
|
258
259
|
}
|
|
259
|
-
const realTarget = safeRealpath(resolved);
|
|
260
|
+
const realTarget = safeRealpath$1(resolved);
|
|
260
261
|
const isRealInsideAllowed = allowedDirs.some(
|
|
261
262
|
(dir) => realTarget.startsWith(dir + path.sep) || realTarget === dir
|
|
262
263
|
);
|
|
@@ -365,7 +366,7 @@ function extractElementNavigationTarget(openingElement) {
|
|
|
365
366
|
}
|
|
366
367
|
if (t.isJSXExpressionContainer(attr.value)) {
|
|
367
368
|
const expr = attr.value.expression;
|
|
368
|
-
const codeStr = generate(expr).code;
|
|
369
|
+
const codeStr = generate$1(expr).code;
|
|
369
370
|
const match = codeStr.match(/(?:navigate|push|replace|jumpTo)\(\s*['"`]([^'"`$]+)['"`]/);
|
|
370
371
|
if (match) return normalizeRouteTarget(match[1]);
|
|
371
372
|
const objMatch = codeStr.match(/(?:pathname|screen|route|name)\s*:\s*['"`]([^'"`$]+)['"`]/);
|
|
@@ -429,7 +430,7 @@ function discoverScreenFilesSync(workspaceRoot) {
|
|
|
429
430
|
if (depth > 4) return;
|
|
430
431
|
let dirents = [];
|
|
431
432
|
try {
|
|
432
|
-
dirents =
|
|
433
|
+
dirents = fs$1.readdirSync(currDir, { withFileTypes: true });
|
|
433
434
|
} catch {
|
|
434
435
|
return;
|
|
435
436
|
}
|
|
@@ -636,7 +637,7 @@ async function discoverScreens(workspaceRoot) {
|
|
|
636
637
|
const extractTriggerLabelFromAncestry = (nodePath, fallbackTarget) => {
|
|
637
638
|
const jsxParent = nodePath.findParent((p) => p.isJSXElement());
|
|
638
639
|
if (jsxParent) {
|
|
639
|
-
const rawCode = generate(jsxParent.node).code;
|
|
640
|
+
const rawCode = generate$1(jsxParent.node).code;
|
|
640
641
|
const textMatches = Array.from(rawCode.matchAll(/>([^<>{}\n]+)</g)).map((m) => m[1].trim()).filter(Boolean);
|
|
641
642
|
if (textMatches.length > 0) {
|
|
642
643
|
return textMatches.slice(0, 2).join(" · ");
|
|
@@ -666,7 +667,7 @@ async function discoverScreens(workspaceRoot) {
|
|
|
666
667
|
}
|
|
667
668
|
}
|
|
668
669
|
if (attrName === "onPress" && t.isJSXExpressionContainer(attr.value)) {
|
|
669
|
-
const exprCode = generate(attr.value.expression).code;
|
|
670
|
+
const exprCode = generate$1(attr.value.expression).code;
|
|
670
671
|
if (/\b(?:goBack|back|pop|popToTop)\s*\(/.test(exprCode)) {
|
|
671
672
|
hasBackNavigation = true;
|
|
672
673
|
backLine = line;
|
|
@@ -931,7 +932,7 @@ async function inspectElement(workspaceRoot, params) {
|
|
|
931
932
|
const textInfo = inspectJsxTextChildren(element);
|
|
932
933
|
const styleInfo = resolveElementStyles(opening, styleSheetInfo);
|
|
933
934
|
const navigationTarget = extractElementNavigationTarget(opening);
|
|
934
|
-
const snippetRaw = generate(element, { concise: false }).code;
|
|
935
|
+
const snippetRaw = generate$1(element, { concise: false }).code;
|
|
935
936
|
const codeSnippet = snippetRaw.length > 600 ? `${snippetRaw.slice(0, 600)}
|
|
936
937
|
/* ... */` : snippetRaw;
|
|
937
938
|
return {
|
|
@@ -1065,7 +1066,7 @@ async function patchElement(workspaceRoot, payload) {
|
|
|
1065
1066
|
upsertObjectProperty(targetObject, styleKey, styleVal);
|
|
1066
1067
|
}
|
|
1067
1068
|
}
|
|
1068
|
-
const output = generate(ast, {
|
|
1069
|
+
const output = generate$1(ast, {
|
|
1069
1070
|
retainLines: false,
|
|
1070
1071
|
comments: true,
|
|
1071
1072
|
jsescOption: { minimal: true }
|
|
@@ -1201,7 +1202,7 @@ async function reorderElement(workspaceRoot, payload) {
|
|
|
1201
1202
|
}
|
|
1202
1203
|
}
|
|
1203
1204
|
});
|
|
1204
|
-
const output = generate(ast, {
|
|
1205
|
+
const output = generate$1(ast, {
|
|
1205
1206
|
retainLines: false,
|
|
1206
1207
|
comments: true,
|
|
1207
1208
|
jsescOption: { minimal: true }
|
|
@@ -1733,7 +1734,7 @@ async function insertElement(workspaceRoot, payload) {
|
|
|
1733
1734
|
}
|
|
1734
1735
|
}
|
|
1735
1736
|
});
|
|
1736
|
-
const output = generate(ast, {
|
|
1737
|
+
const output = generate$1(ast, {
|
|
1737
1738
|
retainLines: false,
|
|
1738
1739
|
comments: true,
|
|
1739
1740
|
jsescOption: { minimal: true }
|
|
@@ -1810,7 +1811,7 @@ async function duplicateElement(workspaceRoot, payload) {
|
|
|
1810
1811
|
}
|
|
1811
1812
|
}
|
|
1812
1813
|
});
|
|
1813
|
-
const output = generate(ast, {
|
|
1814
|
+
const output = generate$1(ast, {
|
|
1814
1815
|
retainLines: false,
|
|
1815
1816
|
comments: true,
|
|
1816
1817
|
jsescOption: { minimal: true }
|
|
@@ -1878,7 +1879,7 @@ async function deleteElement(workspaceRoot, payload) {
|
|
|
1878
1879
|
throw new Error("Could not find element inside parent children.");
|
|
1879
1880
|
}
|
|
1880
1881
|
siblings.splice(pos, 1);
|
|
1881
|
-
const output = generate(ast, {
|
|
1882
|
+
const output = generate$1(ast, {
|
|
1882
1883
|
retainLines: false,
|
|
1883
1884
|
comments: true,
|
|
1884
1885
|
jsescOption: { minimal: true }
|
|
@@ -1903,6 +1904,1878 @@ async function deleteElement(workspaceRoot, payload) {
|
|
|
1903
1904
|
};
|
|
1904
1905
|
});
|
|
1905
1906
|
}
|
|
1907
|
+
async function undoLastPatch(workspaceRoot) {
|
|
1908
|
+
const entry = undoStack.pop();
|
|
1909
|
+
if (!entry) {
|
|
1910
|
+
return {
|
|
1911
|
+
restoredFile: null,
|
|
1912
|
+
description: null,
|
|
1913
|
+
undoAvailable: 0,
|
|
1914
|
+
redoAvailable: redoStack.length
|
|
1915
|
+
};
|
|
1916
|
+
}
|
|
1917
|
+
const fullPath = resolveSafeScreenPath(workspaceRoot, entry.file);
|
|
1918
|
+
await withFileLock(fullPath, async () => {
|
|
1919
|
+
await fs.writeFile(fullPath, entry.previousContent, "utf-8");
|
|
1920
|
+
});
|
|
1921
|
+
redoStack.push(entry);
|
|
1922
|
+
return {
|
|
1923
|
+
restoredFile: entry.file,
|
|
1924
|
+
description: entry.description,
|
|
1925
|
+
undoAvailable: undoStack.length,
|
|
1926
|
+
redoAvailable: redoStack.length
|
|
1927
|
+
};
|
|
1928
|
+
}
|
|
1929
|
+
async function redoLastPatch(workspaceRoot) {
|
|
1930
|
+
const entry = redoStack.pop();
|
|
1931
|
+
if (!entry) {
|
|
1932
|
+
return {
|
|
1933
|
+
restoredFile: null,
|
|
1934
|
+
description: null,
|
|
1935
|
+
undoAvailable: undoStack.length,
|
|
1936
|
+
redoAvailable: 0
|
|
1937
|
+
};
|
|
1938
|
+
}
|
|
1939
|
+
const fullPath = resolveSafeScreenPath(workspaceRoot, entry.file);
|
|
1940
|
+
await withFileLock(fullPath, async () => {
|
|
1941
|
+
await fs.writeFile(fullPath, entry.nextContent, "utf-8");
|
|
1942
|
+
});
|
|
1943
|
+
undoStack.push(entry);
|
|
1944
|
+
return {
|
|
1945
|
+
restoredFile: entry.file,
|
|
1946
|
+
description: entry.description,
|
|
1947
|
+
undoAvailable: undoStack.length,
|
|
1948
|
+
redoAvailable: redoStack.length
|
|
1949
|
+
};
|
|
1950
|
+
}
|
|
1951
|
+
function getHistoryStatus() {
|
|
1952
|
+
return {
|
|
1953
|
+
undoAvailable: undoStack.length,
|
|
1954
|
+
redoAvailable: redoStack.length
|
|
1955
|
+
};
|
|
1956
|
+
}
|
|
1957
|
+
const MAX_BODY_BYTES = 1e6;
|
|
1958
|
+
const LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["127.0.0.1", "localhost", "[::1]", "::1"]);
|
|
1959
|
+
const FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
1960
|
+
function isLoopbackHostHeader(hostHeader) {
|
|
1961
|
+
if (!hostHeader) return false;
|
|
1962
|
+
const trimmed = hostHeader.trim().toLowerCase();
|
|
1963
|
+
if (trimmed.startsWith("[")) {
|
|
1964
|
+
const closeIdx = trimmed.indexOf("]");
|
|
1965
|
+
if (closeIdx === -1) return false;
|
|
1966
|
+
const ipv6Host = trimmed.slice(0, closeIdx + 1);
|
|
1967
|
+
return LOOPBACK_HOSTS.has(ipv6Host);
|
|
1968
|
+
}
|
|
1969
|
+
const hostname = trimmed.split(":")[0];
|
|
1970
|
+
return LOOPBACK_HOSTS.has(hostname);
|
|
1971
|
+
}
|
|
1972
|
+
function verifyApiRequestSecurity(req) {
|
|
1973
|
+
const host = typeof req.headers.host === "string" ? req.headers.host : void 0;
|
|
1974
|
+
if (!isLoopbackHostHeader(host)) {
|
|
1975
|
+
return {
|
|
1976
|
+
allowed: false,
|
|
1977
|
+
reason: "Forbidden Host header: Local API only accepts loopback connections (127.0.0.1, localhost, [::1])"
|
|
1978
|
+
};
|
|
1979
|
+
}
|
|
1980
|
+
const secFetchSite = typeof req.headers["sec-fetch-site"] === "string" ? req.headers["sec-fetch-site"].toLowerCase() : void 0;
|
|
1981
|
+
if (secFetchSite && secFetchSite !== "same-origin" && secFetchSite !== "none") {
|
|
1982
|
+
return {
|
|
1983
|
+
allowed: false,
|
|
1984
|
+
reason: `Forbidden cross-site request (Sec-Fetch-Site: ${secFetchSite})`
|
|
1985
|
+
};
|
|
1986
|
+
}
|
|
1987
|
+
const origin = typeof req.headers.origin === "string" ? req.headers.origin : void 0;
|
|
1988
|
+
if (origin) {
|
|
1989
|
+
try {
|
|
1990
|
+
const parsedOrigin = new URL(origin);
|
|
1991
|
+
if (!LOOPBACK_HOSTS.has(parsedOrigin.hostname.toLowerCase())) {
|
|
1992
|
+
return {
|
|
1993
|
+
allowed: false,
|
|
1994
|
+
reason: `Forbidden cross-origin request from ${origin}`
|
|
1995
|
+
};
|
|
1996
|
+
}
|
|
1997
|
+
} catch {
|
|
1998
|
+
return {
|
|
1999
|
+
allowed: false,
|
|
2000
|
+
reason: "Malformed Origin header"
|
|
2001
|
+
};
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
if (req.method === "POST") {
|
|
2005
|
+
const contentType = typeof req.headers["content-type"] === "string" ? req.headers["content-type"].toLowerCase() : "";
|
|
2006
|
+
if (!contentType.startsWith("application/json")) {
|
|
2007
|
+
return {
|
|
2008
|
+
allowed: false,
|
|
2009
|
+
reason: "POST requests to /api/* must specify Content-Type: application/json"
|
|
2010
|
+
};
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
return { allowed: true };
|
|
2014
|
+
}
|
|
2015
|
+
function sanitizeLayoutPayload(input) {
|
|
2016
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
2017
|
+
throw new Error("Invalid layout payload: expected an object map of screen coordinates");
|
|
2018
|
+
}
|
|
2019
|
+
const sanitized = /* @__PURE__ */ Object.create(null);
|
|
2020
|
+
const entries = Object.entries(input);
|
|
2021
|
+
if (entries.length > 200) {
|
|
2022
|
+
throw new Error("Invalid layout payload: too many screen entries");
|
|
2023
|
+
}
|
|
2024
|
+
for (const [screenId, rawBox] of entries) {
|
|
2025
|
+
if (!screenId || FORBIDDEN_KEYS.has(screenId) || screenId.length > 120) {
|
|
2026
|
+
throw new Error(`Invalid screen key in layout payload: "${screenId}"`);
|
|
2027
|
+
}
|
|
2028
|
+
if (!rawBox || typeof rawBox !== "object" || Array.isArray(rawBox)) {
|
|
2029
|
+
throw new Error(`Invalid layout coordinates for screen "${screenId}"`);
|
|
2030
|
+
}
|
|
2031
|
+
const box = rawBox;
|
|
2032
|
+
const x = Number(box.x);
|
|
2033
|
+
const y = Number(box.y);
|
|
2034
|
+
const width = Number(box.width);
|
|
2035
|
+
const height = Number(box.height);
|
|
2036
|
+
if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(width) || !Number.isFinite(height) || Math.abs(x) > 1e5 || Math.abs(y) > 1e5 || width < 100 || width > 5e3 || height < 100 || height > 5e3) {
|
|
2037
|
+
throw new Error(`Out-of-bounds layout coordinates for screen "${screenId}"`);
|
|
2038
|
+
}
|
|
2039
|
+
sanitized[screenId] = {
|
|
2040
|
+
x: Math.round(x),
|
|
2041
|
+
y: Math.round(y),
|
|
2042
|
+
width: Math.round(width),
|
|
2043
|
+
height: Math.round(height)
|
|
2044
|
+
};
|
|
2045
|
+
}
|
|
2046
|
+
return sanitized;
|
|
2047
|
+
}
|
|
2048
|
+
async function readJsonBody(req) {
|
|
2049
|
+
return new Promise((resolve, reject) => {
|
|
2050
|
+
let raw = "";
|
|
2051
|
+
let byteCount = 0;
|
|
2052
|
+
let aborted = false;
|
|
2053
|
+
req.on("data", (chunk) => {
|
|
2054
|
+
if (aborted) return;
|
|
2055
|
+
const chunkBytes = typeof chunk === "string" ? Buffer.byteLength(chunk, "utf-8") : chunk.length;
|
|
2056
|
+
byteCount += chunkBytes;
|
|
2057
|
+
if (byteCount > MAX_BODY_BYTES) {
|
|
2058
|
+
aborted = true;
|
|
2059
|
+
req.destroy();
|
|
2060
|
+
reject(new Error("Request payload too large"));
|
|
2061
|
+
return;
|
|
2062
|
+
}
|
|
2063
|
+
raw += typeof chunk === "string" ? chunk : chunk.toString("utf-8");
|
|
2064
|
+
});
|
|
2065
|
+
req.on("end", () => {
|
|
2066
|
+
if (aborted) return;
|
|
2067
|
+
try {
|
|
2068
|
+
resolve(raw ? JSON.parse(raw) : {});
|
|
2069
|
+
} catch (err) {
|
|
2070
|
+
reject(err);
|
|
2071
|
+
}
|
|
2072
|
+
});
|
|
2073
|
+
req.on("error", (err) => {
|
|
2074
|
+
if (!aborted) {
|
|
2075
|
+
aborted = true;
|
|
2076
|
+
reject(err);
|
|
2077
|
+
}
|
|
2078
|
+
});
|
|
2079
|
+
});
|
|
2080
|
+
}
|
|
2081
|
+
const generate = _generate.default ?? _generate;
|
|
2082
|
+
const ALLOWED_SCREEN_PREFIXES = ["src/screens/", "screens/", "app/", "src/app/"];
|
|
2083
|
+
const ASSET_EXT_REGEX = /\.(png|jpe?g|gif|webp|svg|avif|bmp|ico|ttf|otf|woff2?|mp3|mp4|wav|m4a|webm|lottie|pdf|bin)$/i;
|
|
2084
|
+
const RESOLVABLE_EXTENSIONS = [
|
|
2085
|
+
".web.tsx",
|
|
2086
|
+
".web.ts",
|
|
2087
|
+
".web.jsx",
|
|
2088
|
+
".web.js",
|
|
2089
|
+
".tsx",
|
|
2090
|
+
".ts",
|
|
2091
|
+
".jsx",
|
|
2092
|
+
".js",
|
|
2093
|
+
".native.tsx",
|
|
2094
|
+
".native.ts",
|
|
2095
|
+
".native.jsx",
|
|
2096
|
+
".native.js",
|
|
2097
|
+
".ios.tsx",
|
|
2098
|
+
".ios.ts",
|
|
2099
|
+
".android.tsx",
|
|
2100
|
+
".android.ts",
|
|
2101
|
+
".mjs",
|
|
2102
|
+
".cjs",
|
|
2103
|
+
".json",
|
|
2104
|
+
".css"
|
|
2105
|
+
];
|
|
2106
|
+
const ZELUNE_JSX_REACT_IDENT = "__zeluneReact";
|
|
2107
|
+
function stripJsonCommentsAndTrailingCommas(input) {
|
|
2108
|
+
const withoutComments = input.replace(
|
|
2109
|
+
/("(?:\\.|[^"\\])*")|\/\*[\s\S]*?\*\/|\/\/.*/g,
|
|
2110
|
+
(_match, quotedString) => quotedString ?? ""
|
|
2111
|
+
);
|
|
2112
|
+
return withoutComments.replace(/,\s*([}\]])/g, "$1");
|
|
2113
|
+
}
|
|
2114
|
+
function tryResolveLocalModule(candidateBase) {
|
|
2115
|
+
try {
|
|
2116
|
+
const cleanBase = candidateBase.split("?")[0];
|
|
2117
|
+
if (fs$1.existsSync(cleanBase) && fs$1.statSync(cleanBase).isFile()) {
|
|
2118
|
+
return cleanBase;
|
|
2119
|
+
}
|
|
2120
|
+
for (const ext of RESOLVABLE_EXTENSIONS) {
|
|
2121
|
+
const withExt = cleanBase + ext;
|
|
2122
|
+
if (fs$1.existsSync(withExt) && fs$1.statSync(withExt).isFile()) {
|
|
2123
|
+
return withExt;
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
if (fs$1.existsSync(cleanBase) && fs$1.statSync(cleanBase).isDirectory()) {
|
|
2127
|
+
for (const ext of RESOLVABLE_EXTENSIONS) {
|
|
2128
|
+
const indexFile = path.join(cleanBase, `index${ext}`);
|
|
2129
|
+
if (fs$1.existsSync(indexFile) && fs$1.statSync(indexFile).isFile()) {
|
|
2130
|
+
return indexFile;
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
} catch {
|
|
2135
|
+
}
|
|
2136
|
+
return null;
|
|
2137
|
+
}
|
|
2138
|
+
function loadTsconfigAliases(roots) {
|
|
2139
|
+
var _a, _b;
|
|
2140
|
+
const mappings = [];
|
|
2141
|
+
for (const rootDir of roots) {
|
|
2142
|
+
try {
|
|
2143
|
+
const tsconfigPath = path.join(rootDir, "tsconfig.json");
|
|
2144
|
+
if (!fs$1.existsSync(tsconfigPath)) continue;
|
|
2145
|
+
const raw = fs$1.readFileSync(tsconfigPath, "utf-8");
|
|
2146
|
+
const parsed = JSON.parse(stripJsonCommentsAndTrailingCommas(raw));
|
|
2147
|
+
const baseUrl = ((_a = parsed.compilerOptions) == null ? void 0 : _a.baseUrl) ?? ".";
|
|
2148
|
+
const paths = ((_b = parsed.compilerOptions) == null ? void 0 : _b.paths) ?? {};
|
|
2149
|
+
for (const [pattern, targets] of Object.entries(paths)) {
|
|
2150
|
+
if (!Array.isArray(targets)) continue;
|
|
2151
|
+
const cleanPrefix = pattern.replace(/\/\*$/, "");
|
|
2152
|
+
if (!cleanPrefix) continue;
|
|
2153
|
+
for (const target of targets) {
|
|
2154
|
+
const cleanTarget = target.replace(/\/\*$/, "");
|
|
2155
|
+
const resolvedDir = path.resolve(rootDir, baseUrl, cleanTarget);
|
|
2156
|
+
mappings.push({ prefix: cleanPrefix, targetDir: resolvedDir });
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
} catch {
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
return mappings;
|
|
2163
|
+
}
|
|
2164
|
+
function cleanJsxText(rawText) {
|
|
2165
|
+
const lines = rawText.split(/\r\n|\n|\r/);
|
|
2166
|
+
let lastNonEmptyLine = 0;
|
|
2167
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2168
|
+
if (/[^ \t]/.test(lines[i])) {
|
|
2169
|
+
lastNonEmptyLine = i;
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
let str = "";
|
|
2173
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2174
|
+
const line = lines[i];
|
|
2175
|
+
const isFirstLine = i === 0;
|
|
2176
|
+
const isLastLine = i === lines.length - 1;
|
|
2177
|
+
const isLastNonEmptyLine = i === lastNonEmptyLine;
|
|
2178
|
+
let trimmedLine = line.replace(/\t/g, " ");
|
|
2179
|
+
if (!isFirstLine) {
|
|
2180
|
+
trimmedLine = trimmedLine.replace(/^[ ]+/, "");
|
|
2181
|
+
}
|
|
2182
|
+
if (!isLastLine) {
|
|
2183
|
+
trimmedLine = trimmedLine.replace(/[ ]+$/, "");
|
|
2184
|
+
}
|
|
2185
|
+
if (trimmedLine) {
|
|
2186
|
+
if (!isLastNonEmptyLine) {
|
|
2187
|
+
trimmedLine += " ";
|
|
2188
|
+
}
|
|
2189
|
+
str += trimmedLine;
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
return str.length > 0 ? str : null;
|
|
2193
|
+
}
|
|
2194
|
+
function convertJsxElementNameToExpr(nameNode) {
|
|
2195
|
+
if (t.isJSXIdentifier(nameNode)) {
|
|
2196
|
+
if (nameNode.name === "this") {
|
|
2197
|
+
return t.thisExpression();
|
|
2198
|
+
}
|
|
2199
|
+
if (/^[a-z]/.test(nameNode.name) || nameNode.name.includes("-")) {
|
|
2200
|
+
return t.stringLiteral(nameNode.name);
|
|
2201
|
+
}
|
|
2202
|
+
return t.identifier(nameNode.name);
|
|
2203
|
+
}
|
|
2204
|
+
if (t.isJSXMemberExpression(nameNode)) {
|
|
2205
|
+
return t.memberExpression(
|
|
2206
|
+
convertJsxElementNameToExpr(nameNode.object),
|
|
2207
|
+
t.identifier(nameNode.property.name)
|
|
2208
|
+
);
|
|
2209
|
+
}
|
|
2210
|
+
if (t.isJSXNamespacedName(nameNode)) {
|
|
2211
|
+
return t.stringLiteral(`${nameNode.namespace.name}:${nameNode.name.name}`);
|
|
2212
|
+
}
|
|
2213
|
+
return t.stringLiteral("div");
|
|
2214
|
+
}
|
|
2215
|
+
function convertJsxAttributesToExpr(attributes, onJsxSeen) {
|
|
2216
|
+
if (attributes.length === 0) {
|
|
2217
|
+
return t.nullLiteral();
|
|
2218
|
+
}
|
|
2219
|
+
const props = [];
|
|
2220
|
+
for (const attr of attributes) {
|
|
2221
|
+
if (t.isJSXSpreadAttribute(attr)) {
|
|
2222
|
+
props.push(t.spreadElement(attr.argument));
|
|
2223
|
+
continue;
|
|
2224
|
+
}
|
|
2225
|
+
const rawKey = t.isJSXIdentifier(attr.name) ? attr.name.name : `${attr.name.namespace.name}:${attr.name.name.name}`;
|
|
2226
|
+
const keyNode = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(rawKey) ? t.identifier(rawKey) : t.stringLiteral(rawKey);
|
|
2227
|
+
let valExpr;
|
|
2228
|
+
if (attr.value === null || attr.value === void 0) {
|
|
2229
|
+
valExpr = t.booleanLiteral(true);
|
|
2230
|
+
} else if (t.isJSXExpressionContainer(attr.value)) {
|
|
2231
|
+
if (t.isJSXEmptyExpression(attr.value.expression)) {
|
|
2232
|
+
valExpr = t.booleanLiteral(true);
|
|
2233
|
+
} else {
|
|
2234
|
+
valExpr = attr.value.expression;
|
|
2235
|
+
}
|
|
2236
|
+
} else if (t.isJSXElement(attr.value) || t.isJSXFragment(attr.value)) {
|
|
2237
|
+
valExpr = transformJsxNode(attr.value, onJsxSeen);
|
|
2238
|
+
} else {
|
|
2239
|
+
valExpr = attr.value;
|
|
2240
|
+
}
|
|
2241
|
+
props.push(t.objectProperty(keyNode, valExpr));
|
|
2242
|
+
}
|
|
2243
|
+
return t.objectExpression(props);
|
|
2244
|
+
}
|
|
2245
|
+
function convertJsxChildrenToExprs(children, onJsxSeen) {
|
|
2246
|
+
const out = [];
|
|
2247
|
+
for (const child of children) {
|
|
2248
|
+
if (!child) continue;
|
|
2249
|
+
if (t.isJSXText(child)) {
|
|
2250
|
+
const cleaned = cleanJsxText(child.value);
|
|
2251
|
+
if (cleaned !== null) {
|
|
2252
|
+
out.push(t.stringLiteral(cleaned));
|
|
2253
|
+
}
|
|
2254
|
+
} else if (t.isJSXExpressionContainer(child)) {
|
|
2255
|
+
if (!t.isJSXEmptyExpression(child.expression)) {
|
|
2256
|
+
out.push(child.expression);
|
|
2257
|
+
}
|
|
2258
|
+
} else if (t.isJSXSpreadChild(child)) {
|
|
2259
|
+
out.push(child.expression);
|
|
2260
|
+
} else if (t.isJSXElement(child) || t.isJSXFragment(child)) {
|
|
2261
|
+
out.push(transformJsxNode(child, onJsxSeen));
|
|
2262
|
+
} else if (t.isSpreadElement(child) || t.isExpression(child)) {
|
|
2263
|
+
out.push(child);
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
return out;
|
|
2267
|
+
}
|
|
2268
|
+
function transformJsxNode(node, onJsxSeen) {
|
|
2269
|
+
onJsxSeen();
|
|
2270
|
+
const createElMember = t.memberExpression(
|
|
2271
|
+
t.identifier(ZELUNE_JSX_REACT_IDENT),
|
|
2272
|
+
t.identifier("createElement")
|
|
2273
|
+
);
|
|
2274
|
+
if (t.isJSXFragment(node)) {
|
|
2275
|
+
const fragMember = t.memberExpression(
|
|
2276
|
+
t.identifier(ZELUNE_JSX_REACT_IDENT),
|
|
2277
|
+
t.identifier("Fragment")
|
|
2278
|
+
);
|
|
2279
|
+
const children2 = convertJsxChildrenToExprs(node.children, onJsxSeen);
|
|
2280
|
+
return t.callExpression(createElMember, [fragMember, t.nullLiteral(), ...children2]);
|
|
2281
|
+
}
|
|
2282
|
+
const tagExpr = convertJsxElementNameToExpr(node.openingElement.name);
|
|
2283
|
+
const propsExpr = convertJsxAttributesToExpr(node.openingElement.attributes, onJsxSeen);
|
|
2284
|
+
const children = convertJsxChildrenToExprs(node.children, onJsxSeen);
|
|
2285
|
+
return t.callExpression(createElMember, [tagExpr, propsExpr, ...children]);
|
|
2286
|
+
}
|
|
2287
|
+
function convertTsEnumToVar(enumNode) {
|
|
2288
|
+
const props = [];
|
|
2289
|
+
let nextNumericValue = 0;
|
|
2290
|
+
for (const member of enumNode.members) {
|
|
2291
|
+
const keyNode = t.isIdentifier(member.id) ? t.identifier(member.id.name) : t.stringLiteral(member.id.value);
|
|
2292
|
+
let initExpr;
|
|
2293
|
+
if (member.initializer) {
|
|
2294
|
+
initExpr = member.initializer;
|
|
2295
|
+
if (t.isNumericLiteral(member.initializer)) {
|
|
2296
|
+
nextNumericValue = member.initializer.value + 1;
|
|
2297
|
+
}
|
|
2298
|
+
} else {
|
|
2299
|
+
initExpr = t.numericLiteral(nextNumericValue++);
|
|
2300
|
+
}
|
|
2301
|
+
props.push(t.objectProperty(keyNode, initExpr));
|
|
2302
|
+
}
|
|
2303
|
+
return t.variableDeclaration("const", [
|
|
2304
|
+
t.variableDeclarator(t.identifier(enumNode.id.name), t.objectExpression(props))
|
|
2305
|
+
]);
|
|
2306
|
+
}
|
|
2307
|
+
function isTsDeclarationOnlyNode(item) {
|
|
2308
|
+
const nodeType = item.type;
|
|
2309
|
+
if (nodeType === "TSInterfaceDeclaration" || nodeType === "TSTypeAliasDeclaration" || nodeType === "TSDeclareFunction" || nodeType === "TSDeclareMethod" || nodeType === "TSModuleDeclaration" || nodeType === "TSIndexSignature" || nodeType === "TSAbstractMethodDefinition" || nodeType === "TSAbstractPropertyDefinition" || nodeType === "TSAbstractAccessorProperty") {
|
|
2310
|
+
return true;
|
|
2311
|
+
}
|
|
2312
|
+
if ((nodeType === "ClassMethod" || nodeType === "ClassProperty" || nodeType === "ClassPrivateProperty" || nodeType === "AccessorProperty") && (item.abstract === true || item.declare === true)) {
|
|
2313
|
+
return true;
|
|
2314
|
+
}
|
|
2315
|
+
if (nodeType === "VariableDeclaration" && item.declare === true) {
|
|
2316
|
+
return true;
|
|
2317
|
+
}
|
|
2318
|
+
if (nodeType === "ClassDeclaration" && item.declare === true) {
|
|
2319
|
+
return true;
|
|
2320
|
+
}
|
|
2321
|
+
return false;
|
|
2322
|
+
}
|
|
2323
|
+
function stripTypeScriptAndTransformJsxAst(node, onJsxSeen = () => {
|
|
2324
|
+
}) {
|
|
2325
|
+
var _a, _b, _c, _d, _e;
|
|
2326
|
+
if (!node || typeof node !== "object") return node;
|
|
2327
|
+
if (Array.isArray(node)) {
|
|
2328
|
+
const nextArr = [];
|
|
2329
|
+
for (let i = 0; i < node.length; i++) {
|
|
2330
|
+
const item = node[i];
|
|
2331
|
+
if (!item) continue;
|
|
2332
|
+
if (isTsDeclarationOnlyNode(item)) {
|
|
2333
|
+
continue;
|
|
2334
|
+
}
|
|
2335
|
+
if (item.type === "ImportDeclaration" && item.importKind === "type") {
|
|
2336
|
+
continue;
|
|
2337
|
+
}
|
|
2338
|
+
if (item.type === "ExportAllDeclaration" && item.exportKind === "type") {
|
|
2339
|
+
continue;
|
|
2340
|
+
}
|
|
2341
|
+
if (item.type === "ExportNamedDeclaration") {
|
|
2342
|
+
if (item.exportKind === "type") continue;
|
|
2343
|
+
if (item.declaration && isTsDeclarationOnlyNode(item.declaration)) {
|
|
2344
|
+
continue;
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2347
|
+
if (item.type === "ExportDefaultDeclaration") {
|
|
2348
|
+
if (item.declaration && isTsDeclarationOnlyNode(item.declaration)) {
|
|
2349
|
+
continue;
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
const transformed = stripTypeScriptAndTransformJsxAst(item, onJsxSeen);
|
|
2353
|
+
if (transformed !== null && transformed !== void 0) {
|
|
2354
|
+
nextArr.push(transformed);
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
return nextArr;
|
|
2358
|
+
}
|
|
2359
|
+
while (node && (node.type === "TSAsExpression" || node.type === "TSSatisfiesExpression" || node.type === "TSTypeAssertion" || node.type === "TSNonNullExpression" || node.type === "TSInstantiationExpression")) {
|
|
2360
|
+
node = node.expression;
|
|
2361
|
+
}
|
|
2362
|
+
if (!node || typeof node !== "object") return node;
|
|
2363
|
+
if (node.type === "TSEnumDeclaration") {
|
|
2364
|
+
return convertTsEnumToVar(node);
|
|
2365
|
+
}
|
|
2366
|
+
if (node.type === "ExportNamedDeclaration" && ((_a = node.declaration) == null ? void 0 : _a.type) === "TSEnumDeclaration") {
|
|
2367
|
+
node.declaration = convertTsEnumToVar(node.declaration);
|
|
2368
|
+
}
|
|
2369
|
+
if (node.type === "ImportDeclaration" && Array.isArray(node.specifiers)) {
|
|
2370
|
+
const origCount = node.specifiers.length;
|
|
2371
|
+
node.specifiers = node.specifiers.filter(
|
|
2372
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2373
|
+
(s) => s.importKind !== "type"
|
|
2374
|
+
);
|
|
2375
|
+
if (origCount > 0 && node.specifiers.length === 0) {
|
|
2376
|
+
return null;
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
if (node.type === "ExportNamedDeclaration" && Array.isArray(node.specifiers)) {
|
|
2380
|
+
node.specifiers = node.specifiers.filter(
|
|
2381
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2382
|
+
(s) => s.exportKind !== "type"
|
|
2383
|
+
);
|
|
2384
|
+
if (!node.declaration && node.specifiers.length === 0) {
|
|
2385
|
+
return null;
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
delete node.typeAnnotation;
|
|
2389
|
+
delete node.returnType;
|
|
2390
|
+
delete node.typeParameters;
|
|
2391
|
+
delete node.typeArguments;
|
|
2392
|
+
delete node.implements;
|
|
2393
|
+
delete node.superTypeParameters;
|
|
2394
|
+
delete node.definite;
|
|
2395
|
+
delete node.accessibility;
|
|
2396
|
+
delete node.readonly;
|
|
2397
|
+
delete node.declare;
|
|
2398
|
+
delete node.override;
|
|
2399
|
+
delete node.abstract;
|
|
2400
|
+
delete node.importKind;
|
|
2401
|
+
delete node.exportKind;
|
|
2402
|
+
if (node.type !== "OptionalMemberExpression" && node.type !== "OptionalCallExpression") {
|
|
2403
|
+
delete node.optional;
|
|
2404
|
+
}
|
|
2405
|
+
if ((node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression" || node.type === "ObjectMethod" || node.type === "ClassMethod" || node.type === "ClassPrivateMethod") && Array.isArray(node.params) && ((_b = node.params[0]) == null ? void 0 : _b.type) === "Identifier" && ((_c = node.params[0]) == null ? void 0 : _c.name) === "this") {
|
|
2406
|
+
node.params = node.params.slice(1);
|
|
2407
|
+
}
|
|
2408
|
+
if (node.type === "ClassMethod" && node.kind === "constructor" && Array.isArray(node.params) && ((_d = node.body) == null ? void 0 : _d.type) === "BlockStatement" && Array.isArray(node.body.body)) {
|
|
2409
|
+
const paramPropAssignments = [];
|
|
2410
|
+
for (const p of node.params) {
|
|
2411
|
+
if ((p == null ? void 0 : p.type) === "TSParameterProperty") {
|
|
2412
|
+
const inner = p.parameter;
|
|
2413
|
+
const propName = (inner == null ? void 0 : inner.type) === "Identifier" ? inner.name : (inner == null ? void 0 : inner.type) === "AssignmentPattern" && ((_e = inner.left) == null ? void 0 : _e.type) === "Identifier" ? inner.left.name : null;
|
|
2414
|
+
if (propName) {
|
|
2415
|
+
paramPropAssignments.push(
|
|
2416
|
+
t.expressionStatement(
|
|
2417
|
+
t.assignmentExpression(
|
|
2418
|
+
"=",
|
|
2419
|
+
t.memberExpression(t.thisExpression(), t.identifier(propName)),
|
|
2420
|
+
t.identifier(propName)
|
|
2421
|
+
)
|
|
2422
|
+
)
|
|
2423
|
+
);
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
if (paramPropAssignments.length > 0) {
|
|
2428
|
+
const superIdx = node.body.body.findIndex(
|
|
2429
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2430
|
+
(s) => {
|
|
2431
|
+
var _a2, _b2;
|
|
2432
|
+
return (s == null ? void 0 : s.type) === "ExpressionStatement" && ((_a2 = s.expression) == null ? void 0 : _a2.type) === "CallExpression" && ((_b2 = s.expression.callee) == null ? void 0 : _b2.type) === "Super";
|
|
2433
|
+
}
|
|
2434
|
+
);
|
|
2435
|
+
const insertPos = superIdx >= 0 ? superIdx + 1 : 0;
|
|
2436
|
+
node.body.body.splice(insertPos, 0, ...paramPropAssignments);
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
if (node.type === "TSParameterProperty") {
|
|
2440
|
+
return stripTypeScriptAndTransformJsxAst(node.parameter, onJsxSeen);
|
|
2441
|
+
}
|
|
2442
|
+
for (const key of Object.keys(node)) {
|
|
2443
|
+
if (key === "loc" || key === "comments" || key === "leadingComments" || key === "trailingComments") {
|
|
2444
|
+
continue;
|
|
2445
|
+
}
|
|
2446
|
+
const val = node[key];
|
|
2447
|
+
if (val && typeof val === "object") {
|
|
2448
|
+
node[key] = stripTypeScriptAndTransformJsxAst(val, onJsxSeen);
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
if (node.type === "JSXElement" || node.type === "JSXFragment") {
|
|
2452
|
+
return transformJsxNode(node, onJsxSeen);
|
|
2453
|
+
}
|
|
2454
|
+
return node;
|
|
2455
|
+
}
|
|
2456
|
+
function injectScreenAgyAttributes(ast, relPath) {
|
|
2457
|
+
let jsxIndex = 0;
|
|
2458
|
+
let hasJsx = false;
|
|
2459
|
+
function visit(node) {
|
|
2460
|
+
var _a, _b;
|
|
2461
|
+
if (!node || typeof node !== "object" || typeof node.type !== "string") return;
|
|
2462
|
+
const typed = node;
|
|
2463
|
+
if (typed.type === "JSXFragment") {
|
|
2464
|
+
hasJsx = true;
|
|
2465
|
+
} else if (typed.type === "JSXOpeningElement") {
|
|
2466
|
+
hasJsx = true;
|
|
2467
|
+
const tagName = getJsxTagName(typed.name);
|
|
2468
|
+
if (tagName !== "Fragment" && tagName !== "React.Fragment" && tagName !== "") {
|
|
2469
|
+
const hasAgyAttr = typed.attributes.some(
|
|
2470
|
+
(attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name, { name: "data-agy-ast-path" })
|
|
2471
|
+
);
|
|
2472
|
+
if (!hasAgyAttr) {
|
|
2473
|
+
const currentIndex = String(jsxIndex++);
|
|
2474
|
+
const line = String(((_a = typed.loc) == null ? void 0 : _a.start.line) ?? 1);
|
|
2475
|
+
const col = String(((_b = typed.loc) == null ? void 0 : _b.start.column) ?? 0);
|
|
2476
|
+
const dataSetProps = [
|
|
2477
|
+
t.objectProperty(t.identifier("agyFile"), t.stringLiteral(relPath)),
|
|
2478
|
+
t.objectProperty(t.identifier("agyLine"), t.stringLiteral(line)),
|
|
2479
|
+
t.objectProperty(t.identifier("agyCol"), t.stringLiteral(col)),
|
|
2480
|
+
t.objectProperty(t.identifier("agyAstPath"), t.stringLiteral(currentIndex)),
|
|
2481
|
+
t.objectProperty(t.identifier("agyTag"), t.stringLiteral(tagName))
|
|
2482
|
+
];
|
|
2483
|
+
typed.attributes.push(
|
|
2484
|
+
t.jsxAttribute(
|
|
2485
|
+
t.jsxIdentifier("dataSet"),
|
|
2486
|
+
t.jsxExpressionContainer(t.objectExpression(dataSetProps))
|
|
2487
|
+
),
|
|
2488
|
+
t.jsxAttribute(t.jsxIdentifier("data-agy-file"), t.stringLiteral(relPath)),
|
|
2489
|
+
t.jsxAttribute(t.jsxIdentifier("data-agy-line"), t.stringLiteral(line)),
|
|
2490
|
+
t.jsxAttribute(t.jsxIdentifier("data-agy-col"), t.stringLiteral(col)),
|
|
2491
|
+
t.jsxAttribute(t.jsxIdentifier("data-agy-ast-path"), t.stringLiteral(currentIndex)),
|
|
2492
|
+
t.jsxAttribute(t.jsxIdentifier("data-agy-tag"), t.stringLiteral(tagName))
|
|
2493
|
+
);
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
const keys = t.VISITOR_KEYS[typed.type];
|
|
2498
|
+
if (!keys) return;
|
|
2499
|
+
const rec = typed;
|
|
2500
|
+
for (const key of keys) {
|
|
2501
|
+
const child = rec[key];
|
|
2502
|
+
if (Array.isArray(child)) {
|
|
2503
|
+
for (let i = 0; i < child.length; i++) {
|
|
2504
|
+
if (child[i]) visit(child[i]);
|
|
2505
|
+
}
|
|
2506
|
+
} else if (child && typeof child === "object") {
|
|
2507
|
+
visit(child);
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
visit(ast);
|
|
2512
|
+
return hasJsx;
|
|
2513
|
+
}
|
|
2514
|
+
function collectNamespaceMemberAccesses(ast, namespaceLocalNames) {
|
|
2515
|
+
const accesses = /* @__PURE__ */ new Map();
|
|
2516
|
+
if (namespaceLocalNames.size === 0) return accesses;
|
|
2517
|
+
for (const name of namespaceLocalNames) {
|
|
2518
|
+
accesses.set(name, /* @__PURE__ */ new Set());
|
|
2519
|
+
}
|
|
2520
|
+
function visit(node) {
|
|
2521
|
+
var _a;
|
|
2522
|
+
if (!node || typeof node !== "object" || typeof node.type !== "string") return;
|
|
2523
|
+
const typed = node;
|
|
2524
|
+
if ((typed.type === "MemberExpression" || typed.type === "OptionalMemberExpression") && !typed.computed && typed.object.type === "Identifier" && namespaceLocalNames.has(typed.object.name) && typed.property.type === "Identifier") {
|
|
2525
|
+
(_a = accesses.get(typed.object.name)) == null ? void 0 : _a.add(typed.property.name);
|
|
2526
|
+
}
|
|
2527
|
+
const keys = t.VISITOR_KEYS[typed.type];
|
|
2528
|
+
if (!keys) return;
|
|
2529
|
+
const rec = typed;
|
|
2530
|
+
for (const key of keys) {
|
|
2531
|
+
const child = rec[key];
|
|
2532
|
+
if (Array.isArray(child)) {
|
|
2533
|
+
for (let i = 0; i < child.length; i++) {
|
|
2534
|
+
if (child[i]) visit(child[i]);
|
|
2535
|
+
}
|
|
2536
|
+
} else if (child && typeof child === "object") {
|
|
2537
|
+
visit(child);
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
visit(ast);
|
|
2542
|
+
return accesses;
|
|
2543
|
+
}
|
|
2544
|
+
function collectRuntimeReferencedIdentifiers(stmts) {
|
|
2545
|
+
const refs = /* @__PURE__ */ new Set();
|
|
2546
|
+
function walk(node) {
|
|
2547
|
+
if (!node || typeof node !== "object") return;
|
|
2548
|
+
if (Array.isArray(node)) {
|
|
2549
|
+
for (let i = 0; i < node.length; i++) {
|
|
2550
|
+
if (node[i]) walk(node[i]);
|
|
2551
|
+
}
|
|
2552
|
+
return;
|
|
2553
|
+
}
|
|
2554
|
+
if (node.type === "Identifier" && typeof node.name === "string") {
|
|
2555
|
+
refs.add(node.name);
|
|
2556
|
+
}
|
|
2557
|
+
for (const key of Object.keys(node)) {
|
|
2558
|
+
if (key === "loc" || key === "comments" || key === "leadingComments" || key === "trailingComments") {
|
|
2559
|
+
continue;
|
|
2560
|
+
}
|
|
2561
|
+
const val = node[key];
|
|
2562
|
+
if (val && typeof val === "object") {
|
|
2563
|
+
walk(val);
|
|
2564
|
+
}
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
for (const stmt of stmts) {
|
|
2568
|
+
if (stmt && stmt.type !== "ImportDeclaration") {
|
|
2569
|
+
walk(stmt);
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
return refs;
|
|
2573
|
+
}
|
|
2574
|
+
function getLocalTypeOnlyDeclaredNames(localFilePath) {
|
|
2575
|
+
var _a;
|
|
2576
|
+
const typeNames = /* @__PURE__ */ new Set();
|
|
2577
|
+
const valueNames = /* @__PURE__ */ new Set();
|
|
2578
|
+
try {
|
|
2579
|
+
if (!/\.(tsx?|mts|cts)$/i.test(localFilePath) || !fs$1.existsSync(localFilePath)) {
|
|
2580
|
+
return typeNames;
|
|
2581
|
+
}
|
|
2582
|
+
const raw = fs$1.readFileSync(localFilePath, "utf-8");
|
|
2583
|
+
const parsed = parse(raw, {
|
|
2584
|
+
sourceType: "module",
|
|
2585
|
+
plugins: ["typescript", "jsx", "decorators-legacy"],
|
|
2586
|
+
errorRecovery: true
|
|
2587
|
+
});
|
|
2588
|
+
for (const stmt of parsed.program.body) {
|
|
2589
|
+
if (stmt.type === "ExportNamedDeclaration") {
|
|
2590
|
+
if (stmt.exportKind === "type") {
|
|
2591
|
+
if (stmt.declaration && "id" in stmt.declaration && ((_a = stmt.declaration.id) == null ? void 0 : _a.type) === "Identifier") {
|
|
2592
|
+
typeNames.add(stmt.declaration.id.name);
|
|
2593
|
+
}
|
|
2594
|
+
for (const s of stmt.specifiers) {
|
|
2595
|
+
if (s.type === "ExportSpecifier") {
|
|
2596
|
+
const exp = s.exported.type === "Identifier" ? s.exported.name : s.exported.value;
|
|
2597
|
+
typeNames.add(exp);
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
continue;
|
|
2601
|
+
}
|
|
2602
|
+
const decl = stmt.declaration;
|
|
2603
|
+
if (decl) {
|
|
2604
|
+
if (decl.type === "TSInterfaceDeclaration" || decl.type === "TSTypeAliasDeclaration") {
|
|
2605
|
+
typeNames.add(decl.id.name);
|
|
2606
|
+
} else if ((decl.type === "FunctionDeclaration" || decl.type === "ClassDeclaration" || decl.type === "TSEnumDeclaration") && decl.id) {
|
|
2607
|
+
valueNames.add(decl.id.name);
|
|
2608
|
+
} else if (decl.type === "VariableDeclaration") {
|
|
2609
|
+
for (const d of decl.declarations) {
|
|
2610
|
+
if (d.id.type === "Identifier") valueNames.add(d.id.name);
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
for (const v of valueNames) {
|
|
2617
|
+
typeNames.delete(v);
|
|
2618
|
+
}
|
|
2619
|
+
} catch {
|
|
2620
|
+
}
|
|
2621
|
+
return typeNames;
|
|
2622
|
+
}
|
|
2623
|
+
function compileModuleForBrowser(options) {
|
|
2624
|
+
const { filePath, targetProjectRoot, workspaceRoot = targetProjectRoot, versionTag } = options;
|
|
2625
|
+
const cleanFilePath = filePath.split("?")[0];
|
|
2626
|
+
const ext = path.extname(cleanFilePath).toLowerCase();
|
|
2627
|
+
if (ext === ".css") {
|
|
2628
|
+
return "export default {};\n";
|
|
2629
|
+
}
|
|
2630
|
+
if (ext === ".json") {
|
|
2631
|
+
const rawJson = fs$1.readFileSync(cleanFilePath, "utf-8");
|
|
2632
|
+
return `export default ${rawJson};
|
|
2633
|
+
`;
|
|
2634
|
+
}
|
|
2635
|
+
if (ASSET_EXT_REGEX.test(ext)) {
|
|
2636
|
+
const assetUrl = `/@zelune/asset?file=${encodeURIComponent(cleanFilePath)}`;
|
|
2637
|
+
return `export default { uri: ${JSON.stringify(assetUrl)} };
|
|
2638
|
+
`;
|
|
2639
|
+
}
|
|
2640
|
+
const code = fs$1.readFileSync(cleanFilePath, "utf-8");
|
|
2641
|
+
const ast = parse(code, {
|
|
2642
|
+
sourceType: "module",
|
|
2643
|
+
plugins: ["typescript", "jsx", "decorators-legacy"],
|
|
2644
|
+
errorRecovery: true
|
|
2645
|
+
});
|
|
2646
|
+
const normalizedTargetRoot = path.resolve(targetProjectRoot);
|
|
2647
|
+
const relPath = path.relative(normalizedTargetRoot, path.resolve(cleanFilePath)).replace(/\\/g, "/");
|
|
2648
|
+
const isScreenFile = ALLOWED_SCREEN_PREFIXES.some((prefix) => relPath.startsWith(prefix));
|
|
2649
|
+
let emittedJsx = false;
|
|
2650
|
+
if (isScreenFile) {
|
|
2651
|
+
if (injectScreenAgyAttributes(ast, relPath)) {
|
|
2652
|
+
emittedJsx = true;
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
const uniqueRoots = Array.from(
|
|
2656
|
+
/* @__PURE__ */ new Set([path.resolve(targetProjectRoot), path.resolve(workspaceRoot)])
|
|
2657
|
+
);
|
|
2658
|
+
const tsconfigAliases = loadTsconfigAliases(uniqueRoots);
|
|
2659
|
+
function resolveAlias(specifier) {
|
|
2660
|
+
for (const mapping of tsconfigAliases) {
|
|
2661
|
+
if (specifier === mapping.prefix) {
|
|
2662
|
+
const found = tryResolveLocalModule(mapping.targetDir);
|
|
2663
|
+
if (found) return found;
|
|
2664
|
+
} else if (specifier.startsWith(mapping.prefix + "/")) {
|
|
2665
|
+
const rel = specifier.slice(mapping.prefix.length + 1);
|
|
2666
|
+
const found = tryResolveLocalModule(path.join(mapping.targetDir, rel));
|
|
2667
|
+
if (found) return found;
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
if (specifier.startsWith("@/") || specifier.startsWith("~/")) {
|
|
2671
|
+
const rel = specifier.slice(2);
|
|
2672
|
+
for (const r of uniqueRoots) {
|
|
2673
|
+
const direct = tryResolveLocalModule(path.join(r, rel));
|
|
2674
|
+
if (direct) return direct;
|
|
2675
|
+
const inSrc = tryResolveLocalModule(path.join(r, "src", rel));
|
|
2676
|
+
if (inSrc) return inSrc;
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
return null;
|
|
2680
|
+
}
|
|
2681
|
+
function resolveLocalOrAliasFile(specifier) {
|
|
2682
|
+
if (specifier.startsWith(".")) {
|
|
2683
|
+
const candidate = path.resolve(path.dirname(cleanFilePath), specifier);
|
|
2684
|
+
return tryResolveLocalModule(candidate);
|
|
2685
|
+
}
|
|
2686
|
+
if (specifier.startsWith("@/") || specifier.startsWith("~/") || tsconfigAliases.some((m) => specifier === m.prefix || specifier.startsWith(m.prefix + "/"))) {
|
|
2687
|
+
return resolveAlias(specifier);
|
|
2688
|
+
}
|
|
2689
|
+
return null;
|
|
2690
|
+
}
|
|
2691
|
+
function rewriteResolvedLocalFile(resolved, requestedExports) {
|
|
2692
|
+
const normalized = resolved.replace(/\\/g, "/");
|
|
2693
|
+
if (normalized.endsWith("/src/runtime/MockNavigationContext.tsx")) {
|
|
2694
|
+
return `/@zelune/shim/stub.js?pkg=zelune-mock-nav&exports=${encodeURIComponent(
|
|
2695
|
+
requestedExports.join(",")
|
|
2696
|
+
)}`;
|
|
2697
|
+
}
|
|
2698
|
+
if (normalized.endsWith("/src/runtime/reactNativeShim.ts")) {
|
|
2699
|
+
return "/@zelune/shim/react-native.js";
|
|
2700
|
+
}
|
|
2701
|
+
const vSuffix = versionTag ? `&t=${encodeURIComponent(versionTag)}` : "";
|
|
2702
|
+
return `/@zelune/module?file=${encodeURIComponent(resolved)}${vSuffix}`;
|
|
2703
|
+
}
|
|
2704
|
+
function rewriteSpecifier(specifier, requestedExports) {
|
|
2705
|
+
if (specifier === "react" || specifier === "react/jsx-runtime" || specifier === "react/jsx-dev-runtime") {
|
|
2706
|
+
return "/@zelune/shim/react.js";
|
|
2707
|
+
}
|
|
2708
|
+
if (specifier === "react-dom" || specifier === "react-dom/client") {
|
|
2709
|
+
return "/@zelune/shim/react-dom.js";
|
|
2710
|
+
}
|
|
2711
|
+
if (specifier === "react-native" || specifier === "react-native-web") {
|
|
2712
|
+
return "/@zelune/shim/react-native.js";
|
|
2713
|
+
}
|
|
2714
|
+
if (specifier === "lucide-react" || specifier === "lucide-react-native") {
|
|
2715
|
+
return `/@zelune/shim/lucide.js?exports=${encodeURIComponent(requestedExports.join(","))}`;
|
|
2716
|
+
}
|
|
2717
|
+
if (specifier.startsWith(".") || specifier.startsWith("@/") || specifier.startsWith("~/") || tsconfigAliases.some((m) => specifier === m.prefix || specifier.startsWith(m.prefix + "/"))) {
|
|
2718
|
+
const resolved = resolveLocalOrAliasFile(specifier);
|
|
2719
|
+
if (resolved) {
|
|
2720
|
+
return rewriteResolvedLocalFile(resolved, requestedExports);
|
|
2721
|
+
}
|
|
2722
|
+
return `/@zelune/shim/stub.js?pkg=${encodeURIComponent(specifier)}&exports=${encodeURIComponent(
|
|
2723
|
+
requestedExports.join(",")
|
|
2724
|
+
)}`;
|
|
2725
|
+
}
|
|
2726
|
+
return `/@zelune/shim/stub.js?pkg=${encodeURIComponent(specifier)}&exports=${encodeURIComponent(
|
|
2727
|
+
requestedExports.join(",")
|
|
2728
|
+
)}`;
|
|
2729
|
+
}
|
|
2730
|
+
const sideEffectImportNodes = /* @__PURE__ */ new WeakSet();
|
|
2731
|
+
for (const stmt of ast.program.body) {
|
|
2732
|
+
if (stmt.type === "ImportDeclaration" && stmt.specifiers.length === 0 && stmt.importKind !== "type") {
|
|
2733
|
+
sideEffectImportNodes.add(stmt);
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
stripTypeScriptAndTransformJsxAst(ast, () => {
|
|
2737
|
+
emittedJsx = true;
|
|
2738
|
+
});
|
|
2739
|
+
const hoistedRequireImports = [];
|
|
2740
|
+
let reqCounter = 0;
|
|
2741
|
+
function transformRequireCalls(node) {
|
|
2742
|
+
var _a, _b;
|
|
2743
|
+
if (!node || typeof node !== "object") return node;
|
|
2744
|
+
if (Array.isArray(node)) {
|
|
2745
|
+
for (let i = 0; i < node.length; i++) {
|
|
2746
|
+
if (node[i]) node[i] = transformRequireCalls(node[i]);
|
|
2747
|
+
}
|
|
2748
|
+
return node;
|
|
2749
|
+
}
|
|
2750
|
+
for (const key of Object.keys(node)) {
|
|
2751
|
+
if (key === "loc" || key === "comments" || key === "leadingComments" || key === "trailingComments") {
|
|
2752
|
+
continue;
|
|
2753
|
+
}
|
|
2754
|
+
const val = node[key];
|
|
2755
|
+
if (val && typeof val === "object") {
|
|
2756
|
+
node[key] = transformRequireCalls(val);
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
if (node.type === "CallExpression" && ((_a = node.callee) == null ? void 0 : _a.type) === "Identifier" && node.callee.name === "require" && Array.isArray(node.arguments) && node.arguments.length === 1 && ((_b = node.arguments[0]) == null ? void 0 : _b.type) === "StringLiteral") {
|
|
2760
|
+
const rawSpec = node.arguments[0].value;
|
|
2761
|
+
const localResolved = resolveLocalOrAliasFile(rawSpec);
|
|
2762
|
+
if (localResolved && ASSET_EXT_REGEX.test(path.extname(localResolved))) {
|
|
2763
|
+
const assetUrl = `/@zelune/asset?file=${encodeURIComponent(localResolved)}`;
|
|
2764
|
+
return t.objectExpression([
|
|
2765
|
+
t.objectProperty(t.identifier("uri"), t.stringLiteral(assetUrl))
|
|
2766
|
+
]);
|
|
2767
|
+
}
|
|
2768
|
+
if (ASSET_EXT_REGEX.test(rawSpec)) {
|
|
2769
|
+
return t.objectExpression([
|
|
2770
|
+
t.objectProperty(t.identifier("uri"), t.stringLiteral(""))
|
|
2771
|
+
]);
|
|
2772
|
+
}
|
|
2773
|
+
const reqIdent = t.identifier(`__zeluneReq_${reqCounter++}`);
|
|
2774
|
+
const rewritten = localResolved ? rewriteResolvedLocalFile(localResolved, []) : rewriteSpecifier(rawSpec, []);
|
|
2775
|
+
hoistedRequireImports.push(
|
|
2776
|
+
t.importDeclaration([t.importNamespaceSpecifier(reqIdent)], t.stringLiteral(rewritten))
|
|
2777
|
+
);
|
|
2778
|
+
return t.logicalExpression(
|
|
2779
|
+
"??",
|
|
2780
|
+
t.optionalMemberExpression(reqIdent, t.identifier("default"), false, true),
|
|
2781
|
+
reqIdent
|
|
2782
|
+
);
|
|
2783
|
+
}
|
|
2784
|
+
return node;
|
|
2785
|
+
}
|
|
2786
|
+
transformRequireCalls(ast.program.body);
|
|
2787
|
+
const runtimeReferencedIds = collectRuntimeReferencedIdentifiers(ast.program.body);
|
|
2788
|
+
const elidedBody = [];
|
|
2789
|
+
for (const stmt of ast.program.body) {
|
|
2790
|
+
if (stmt.type === "ImportDeclaration") {
|
|
2791
|
+
if (sideEffectImportNodes.has(stmt)) {
|
|
2792
|
+
elidedBody.push(stmt);
|
|
2793
|
+
continue;
|
|
2794
|
+
}
|
|
2795
|
+
stmt.specifiers = stmt.specifiers.filter((spec) => runtimeReferencedIds.has(spec.local.name));
|
|
2796
|
+
if (stmt.specifiers.length > 0) {
|
|
2797
|
+
elidedBody.push(stmt);
|
|
2798
|
+
}
|
|
2799
|
+
continue;
|
|
2800
|
+
}
|
|
2801
|
+
if (stmt.type === "ExportNamedDeclaration" && stmt.source && !stmt.declaration && stmt.specifiers.length > 0) {
|
|
2802
|
+
const localTarget = resolveLocalOrAliasFile(stmt.source.value);
|
|
2803
|
+
if (localTarget) {
|
|
2804
|
+
const typeOnlyNames = getLocalTypeOnlyDeclaredNames(localTarget);
|
|
2805
|
+
if (typeOnlyNames.size > 0) {
|
|
2806
|
+
stmt.specifiers = stmt.specifiers.filter((spec) => {
|
|
2807
|
+
if (spec.type !== "ExportSpecifier") return true;
|
|
2808
|
+
return !typeOnlyNames.has(spec.local.name);
|
|
2809
|
+
});
|
|
2810
|
+
if (stmt.specifiers.length === 0) {
|
|
2811
|
+
continue;
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
elidedBody.push(stmt);
|
|
2817
|
+
}
|
|
2818
|
+
ast.program.body = elidedBody;
|
|
2819
|
+
const namespaceLocals = /* @__PURE__ */ new Set();
|
|
2820
|
+
for (const stmt of ast.program.body) {
|
|
2821
|
+
if (stmt.type === "ImportDeclaration") {
|
|
2822
|
+
for (const spec of stmt.specifiers) {
|
|
2823
|
+
if (spec.type === "ImportNamespaceSpecifier") {
|
|
2824
|
+
namespaceLocals.add(spec.local.name);
|
|
2825
|
+
}
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
const namespaceMemberMap = collectNamespaceMemberAccesses(ast, namespaceLocals);
|
|
2830
|
+
for (const stmt of ast.program.body) {
|
|
2831
|
+
if (stmt.type === "ImportDeclaration") {
|
|
2832
|
+
const requested = [];
|
|
2833
|
+
for (const spec of stmt.specifiers) {
|
|
2834
|
+
if (spec.type === "ImportSpecifier") {
|
|
2835
|
+
const importedName = spec.imported.type === "Identifier" ? spec.imported.name : spec.imported.value;
|
|
2836
|
+
if (importedName && importedName !== "default") {
|
|
2837
|
+
requested.push(importedName);
|
|
2838
|
+
}
|
|
2839
|
+
} else if (spec.type === "ImportNamespaceSpecifier") {
|
|
2840
|
+
const accessed = namespaceMemberMap.get(spec.local.name);
|
|
2841
|
+
if (accessed) {
|
|
2842
|
+
for (const memberName of accessed) {
|
|
2843
|
+
if (memberName !== "default") requested.push(memberName);
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
stmt.source.value = rewriteSpecifier(stmt.source.value, requested);
|
|
2849
|
+
} else if ((stmt.type === "ExportNamedDeclaration" || stmt.type === "ExportAllDeclaration") && stmt.source) {
|
|
2850
|
+
const requested = [];
|
|
2851
|
+
if (stmt.type === "ExportNamedDeclaration") {
|
|
2852
|
+
for (const spec of stmt.specifiers) {
|
|
2853
|
+
if (spec.type === "ExportSpecifier") {
|
|
2854
|
+
const localName = spec.local.name;
|
|
2855
|
+
const exportedName = spec.exported.type === "Identifier" ? spec.exported.name : spec.exported.value;
|
|
2856
|
+
if (localName && localName !== "default") requested.push(localName);
|
|
2857
|
+
if (exportedName && exportedName !== "default") requested.push(exportedName);
|
|
2858
|
+
}
|
|
2859
|
+
}
|
|
2860
|
+
}
|
|
2861
|
+
stmt.source.value = rewriteSpecifier(stmt.source.value, requested);
|
|
2862
|
+
}
|
|
2863
|
+
}
|
|
2864
|
+
if (hoistedRequireImports.length > 0) {
|
|
2865
|
+
ast.program.body.unshift(...hoistedRequireImports);
|
|
2866
|
+
}
|
|
2867
|
+
if (emittedJsx) {
|
|
2868
|
+
ast.program.body.unshift(
|
|
2869
|
+
t.importDeclaration(
|
|
2870
|
+
[t.importDefaultSpecifier(t.identifier(ZELUNE_JSX_REACT_IDENT))],
|
|
2871
|
+
t.stringLiteral("/@zelune/shim/react.js")
|
|
2872
|
+
)
|
|
2873
|
+
);
|
|
2874
|
+
}
|
|
2875
|
+
const out = generate(ast, {
|
|
2876
|
+
retainLines: false,
|
|
2877
|
+
compact: false
|
|
2878
|
+
});
|
|
2879
|
+
return out.code;
|
|
2880
|
+
}
|
|
2881
|
+
function extractStaticAstValue(node, scope, depth = 0) {
|
|
2882
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
2883
|
+
if (!node || depth > 12) return void 0;
|
|
2884
|
+
switch (node.type) {
|
|
2885
|
+
case "StringLiteral":
|
|
2886
|
+
case "NumericLiteral":
|
|
2887
|
+
case "BooleanLiteral":
|
|
2888
|
+
return node.value;
|
|
2889
|
+
case "NullLiteral":
|
|
2890
|
+
return null;
|
|
2891
|
+
case "UnaryExpression": {
|
|
2892
|
+
const arg = extractStaticAstValue(node.argument, scope, depth + 1);
|
|
2893
|
+
if (typeof arg === "number") {
|
|
2894
|
+
if (node.operator === "-") return -arg;
|
|
2895
|
+
if (node.operator === "+") return +arg;
|
|
2896
|
+
}
|
|
2897
|
+
return void 0;
|
|
2898
|
+
}
|
|
2899
|
+
case "TemplateLiteral": {
|
|
2900
|
+
if (Array.isArray(node.expressions) && node.expressions.length === 0) {
|
|
2901
|
+
return ((_c = (_b = (_a = node.quasis) == null ? void 0 : _a[0]) == null ? void 0 : _b.value) == null ? void 0 : _c.cooked) ?? ((_f = (_e = (_d = node.quasis) == null ? void 0 : _d[0]) == null ? void 0 : _e.value) == null ? void 0 : _f.raw) ?? "";
|
|
2902
|
+
}
|
|
2903
|
+
return void 0;
|
|
2904
|
+
}
|
|
2905
|
+
case "ArrayExpression": {
|
|
2906
|
+
const items = [];
|
|
2907
|
+
for (const el of node.elements ?? []) {
|
|
2908
|
+
if (!el) continue;
|
|
2909
|
+
if (el.type === "SpreadElement") {
|
|
2910
|
+
const spreadVal = extractStaticAstValue(el.argument, scope, depth + 1);
|
|
2911
|
+
if (Array.isArray(spreadVal)) items.push(...spreadVal);
|
|
2912
|
+
} else {
|
|
2913
|
+
const val = extractStaticAstValue(el, scope, depth + 1);
|
|
2914
|
+
if (val !== void 0) items.push(val);
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
return items;
|
|
2918
|
+
}
|
|
2919
|
+
case "ObjectExpression": {
|
|
2920
|
+
const result = {};
|
|
2921
|
+
for (const prop of node.properties ?? []) {
|
|
2922
|
+
if (prop.type === "SpreadElement") {
|
|
2923
|
+
const spreadObj = extractStaticAstValue(prop.argument, scope, depth + 1);
|
|
2924
|
+
if (spreadObj && typeof spreadObj === "object" && !Array.isArray(spreadObj)) {
|
|
2925
|
+
Object.assign(result, spreadObj);
|
|
2926
|
+
}
|
|
2927
|
+
continue;
|
|
2928
|
+
}
|
|
2929
|
+
if (prop.type !== "ObjectProperty" || prop.computed) continue;
|
|
2930
|
+
const key = ((_g = prop.key) == null ? void 0 : _g.type) === "Identifier" ? prop.key.name : ((_h = prop.key) == null ? void 0 : _h.type) === "StringLiteral" || ((_i = prop.key) == null ? void 0 : _i.type) === "NumericLiteral" ? String(prop.key.value) : null;
|
|
2931
|
+
if (!key) continue;
|
|
2932
|
+
const val = extractStaticAstValue(prop.value, scope, depth + 1);
|
|
2933
|
+
if (val !== void 0) {
|
|
2934
|
+
result[key] = val;
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
return result;
|
|
2938
|
+
}
|
|
2939
|
+
case "TSAsExpression":
|
|
2940
|
+
case "TSSatisfiesExpression":
|
|
2941
|
+
case "TSTypeAssertion":
|
|
2942
|
+
case "ParenthesizedExpression":
|
|
2943
|
+
return extractStaticAstValue(node.expression, scope, depth + 1);
|
|
2944
|
+
case "Identifier":
|
|
2945
|
+
return scope.get(node.name);
|
|
2946
|
+
default:
|
|
2947
|
+
return void 0;
|
|
2948
|
+
}
|
|
2949
|
+
}
|
|
2950
|
+
function parseTailwindConfigSource(raw) {
|
|
2951
|
+
try {
|
|
2952
|
+
const ast = parse(raw, {
|
|
2953
|
+
sourceType: "unambiguous",
|
|
2954
|
+
plugins: ["typescript", "jsx"],
|
|
2955
|
+
errorRecovery: true
|
|
2956
|
+
});
|
|
2957
|
+
const scope = /* @__PURE__ */ new Map();
|
|
2958
|
+
let exportedConfig;
|
|
2959
|
+
for (const stmt of ast.program.body) {
|
|
2960
|
+
if (stmt.type === "VariableDeclaration") {
|
|
2961
|
+
for (const decl of stmt.declarations) {
|
|
2962
|
+
if (decl.id.type === "Identifier" && decl.init) {
|
|
2963
|
+
const val = extractStaticAstValue(decl.init, scope);
|
|
2964
|
+
if (val !== void 0) {
|
|
2965
|
+
scope.set(decl.id.name, val);
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2969
|
+
} else if (stmt.type === "ExportDefaultDeclaration") {
|
|
2970
|
+
exportedConfig = extractStaticAstValue(stmt.declaration, scope);
|
|
2971
|
+
} else if (stmt.type === "ExpressionStatement" && stmt.expression.type === "AssignmentExpression") {
|
|
2972
|
+
const left = stmt.expression.left;
|
|
2973
|
+
if (left.type === "MemberExpression" && left.object.type === "Identifier" && left.property.type === "Identifier" && (left.object.name === "module" && left.property.name === "exports" || left.object.name === "exports" && left.property.name === "default")) {
|
|
2974
|
+
exportedConfig = extractStaticAstValue(stmt.expression.right, scope);
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
2977
|
+
}
|
|
2978
|
+
const cfg = exportedConfig;
|
|
2979
|
+
if ((cfg == null ? void 0 : cfg.theme) && typeof cfg.theme === "object") {
|
|
2980
|
+
const baseTheme = { ...cfg.theme };
|
|
2981
|
+
const extendTheme = cfg.theme.extend && typeof cfg.theme.extend === "object" ? cfg.theme.extend : {};
|
|
2982
|
+
delete baseTheme.extend;
|
|
2983
|
+
const merged = { ...baseTheme };
|
|
2984
|
+
for (const [k, v] of Object.entries(extendTheme)) {
|
|
2985
|
+
if (v && typeof v === "object" && !Array.isArray(v)) {
|
|
2986
|
+
merged[k] = {
|
|
2987
|
+
...baseTheme[k] ?? {},
|
|
2988
|
+
...v
|
|
2989
|
+
};
|
|
2990
|
+
} else {
|
|
2991
|
+
merged[k] = v;
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
return merged;
|
|
2995
|
+
}
|
|
2996
|
+
} catch {
|
|
2997
|
+
}
|
|
2998
|
+
return {};
|
|
2999
|
+
}
|
|
3000
|
+
function loadProjectTailwindTheme(roots) {
|
|
3001
|
+
const candidates = [
|
|
3002
|
+
"tailwind.config.js",
|
|
3003
|
+
"tailwind.config.cjs",
|
|
3004
|
+
"tailwind.config.mjs",
|
|
3005
|
+
"tailwind.config.ts"
|
|
3006
|
+
];
|
|
3007
|
+
const uniqueRoots = Array.from(new Set(roots.map((r) => path.resolve(r))));
|
|
3008
|
+
for (const rootDir of uniqueRoots) {
|
|
3009
|
+
for (const fileName of candidates) {
|
|
3010
|
+
const fullPath = path.join(rootDir, fileName);
|
|
3011
|
+
try {
|
|
3012
|
+
if (!fs$1.existsSync(fullPath)) continue;
|
|
3013
|
+
const raw = fs$1.readFileSync(fullPath, "utf-8");
|
|
3014
|
+
const parsedTheme = parseTailwindConfigSource(raw);
|
|
3015
|
+
if (Object.keys(parsedTheme).length > 0) {
|
|
3016
|
+
return parsedTheme;
|
|
3017
|
+
}
|
|
3018
|
+
} catch {
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
}
|
|
3022
|
+
return {};
|
|
3023
|
+
}
|
|
3024
|
+
const MIME_TYPES = {
|
|
3025
|
+
".html": "text/html; charset=utf-8",
|
|
3026
|
+
".js": "application/javascript; charset=utf-8",
|
|
3027
|
+
".mjs": "application/javascript; charset=utf-8",
|
|
3028
|
+
".css": "text/css; charset=utf-8",
|
|
3029
|
+
".json": "application/json; charset=utf-8",
|
|
3030
|
+
".svg": "image/svg+xml",
|
|
3031
|
+
".png": "image/png",
|
|
3032
|
+
".jpg": "image/jpeg",
|
|
3033
|
+
".jpeg": "image/jpeg",
|
|
3034
|
+
".webp": "image/webp",
|
|
3035
|
+
".gif": "image/gif",
|
|
3036
|
+
".avif": "image/avif",
|
|
3037
|
+
".bmp": "image/bmp",
|
|
3038
|
+
".ico": "image/x-icon",
|
|
3039
|
+
".ttf": "font/ttf",
|
|
3040
|
+
".otf": "font/otf",
|
|
3041
|
+
".woff": "font/woff",
|
|
3042
|
+
".woff2": "font/woff2",
|
|
3043
|
+
".mp3": "audio/mpeg",
|
|
3044
|
+
".wav": "audio/wav",
|
|
3045
|
+
".mp4": "video/mp4",
|
|
3046
|
+
".webm": "video/webm"
|
|
3047
|
+
};
|
|
3048
|
+
const REACT_EXPORTS = [
|
|
3049
|
+
"Children",
|
|
3050
|
+
"Component",
|
|
3051
|
+
"Fragment",
|
|
3052
|
+
"Profiler",
|
|
3053
|
+
"PureComponent",
|
|
3054
|
+
"StrictMode",
|
|
3055
|
+
"Suspense",
|
|
3056
|
+
"cloneElement",
|
|
3057
|
+
"createContext",
|
|
3058
|
+
"createElement",
|
|
3059
|
+
"createFactory",
|
|
3060
|
+
"createRef",
|
|
3061
|
+
"forwardRef",
|
|
3062
|
+
"isValidElement",
|
|
3063
|
+
"lazy",
|
|
3064
|
+
"memo",
|
|
3065
|
+
"startTransition",
|
|
3066
|
+
"useCallback",
|
|
3067
|
+
"useContext",
|
|
3068
|
+
"useDebugValue",
|
|
3069
|
+
"useDeferredValue",
|
|
3070
|
+
"useEffect",
|
|
3071
|
+
"useId",
|
|
3072
|
+
"useImperativeHandle",
|
|
3073
|
+
"useInsertionEffect",
|
|
3074
|
+
"useLayoutEffect",
|
|
3075
|
+
"useMemo",
|
|
3076
|
+
"useReducer",
|
|
3077
|
+
"useRef",
|
|
3078
|
+
"useState",
|
|
3079
|
+
"useSyncExternalStore",
|
|
3080
|
+
"useTransition",
|
|
3081
|
+
"version"
|
|
3082
|
+
];
|
|
3083
|
+
const REACT_NATIVE_EXPORTS = [
|
|
3084
|
+
"View",
|
|
3085
|
+
"Text",
|
|
3086
|
+
"Pressable",
|
|
3087
|
+
"TouchableOpacity",
|
|
3088
|
+
"TouchableHighlight",
|
|
3089
|
+
"TouchableWithoutFeedback",
|
|
3090
|
+
"TouchableNativeFeedback",
|
|
3091
|
+
"ScrollView",
|
|
3092
|
+
"FlatList",
|
|
3093
|
+
"SectionList",
|
|
3094
|
+
"VirtualizedList",
|
|
3095
|
+
"KeyboardAvoidingView",
|
|
3096
|
+
"TextInput",
|
|
3097
|
+
"Image",
|
|
3098
|
+
"ImageBackground",
|
|
3099
|
+
"SafeAreaView",
|
|
3100
|
+
"ActivityIndicator",
|
|
3101
|
+
"Switch",
|
|
3102
|
+
"Modal",
|
|
3103
|
+
"Button",
|
|
3104
|
+
"RefreshControl",
|
|
3105
|
+
"StatusBar",
|
|
3106
|
+
"StyleSheet",
|
|
3107
|
+
"Platform",
|
|
3108
|
+
"Dimensions",
|
|
3109
|
+
"useWindowDimensions",
|
|
3110
|
+
"PixelRatio",
|
|
3111
|
+
"Animated",
|
|
3112
|
+
"useAnimatedValue",
|
|
3113
|
+
"Easing",
|
|
3114
|
+
"Alert",
|
|
3115
|
+
"Linking",
|
|
3116
|
+
"Share",
|
|
3117
|
+
"Vibration",
|
|
3118
|
+
"Keyboard",
|
|
3119
|
+
"BackHandler",
|
|
3120
|
+
"AppState",
|
|
3121
|
+
"Appearance",
|
|
3122
|
+
"useColorScheme",
|
|
3123
|
+
"PanResponder",
|
|
3124
|
+
"LayoutAnimation",
|
|
3125
|
+
"UIManager",
|
|
3126
|
+
"InteractionManager",
|
|
3127
|
+
"NativeEventEmitter",
|
|
3128
|
+
"DeviceEventEmitter",
|
|
3129
|
+
"NativeAppEventEmitter",
|
|
3130
|
+
"NativeModules",
|
|
3131
|
+
"I18nManager",
|
|
3132
|
+
"AccessibilityInfo",
|
|
3133
|
+
"LogBox",
|
|
3134
|
+
"YellowBox",
|
|
3135
|
+
"findNodeHandle",
|
|
3136
|
+
"processColor",
|
|
3137
|
+
"PermissionsAndroid",
|
|
3138
|
+
"ToastAndroid",
|
|
3139
|
+
"ActionSheetIOS",
|
|
3140
|
+
"InputAccessoryView",
|
|
3141
|
+
"DrawerLayoutAndroid",
|
|
3142
|
+
"TurboModuleRegistry",
|
|
3143
|
+
"requireNativeComponent",
|
|
3144
|
+
"codegenNativeComponent",
|
|
3145
|
+
"PlatformColor",
|
|
3146
|
+
"DynamicColorIOS",
|
|
3147
|
+
"DevSettings",
|
|
3148
|
+
"Systrace",
|
|
3149
|
+
"Networking",
|
|
3150
|
+
"Settings"
|
|
3151
|
+
];
|
|
3152
|
+
const DEFAULT_MOCK_NAV_EXPORTS = [
|
|
3153
|
+
"MockNavigationProvider",
|
|
3154
|
+
"useNavigation",
|
|
3155
|
+
"useRoute",
|
|
3156
|
+
"useFocusEffect",
|
|
3157
|
+
"useIsFocused",
|
|
3158
|
+
"useScrollToTop",
|
|
3159
|
+
"NavigationContainer",
|
|
3160
|
+
"CommonActions",
|
|
3161
|
+
"StackActions",
|
|
3162
|
+
"createNativeStackNavigator",
|
|
3163
|
+
"createStackNavigator",
|
|
3164
|
+
"createBottomTabNavigator",
|
|
3165
|
+
"createDrawerNavigator",
|
|
3166
|
+
"createMaterialTopTabNavigator",
|
|
3167
|
+
"DefaultTheme",
|
|
3168
|
+
"DarkTheme",
|
|
3169
|
+
"ThemeProvider",
|
|
3170
|
+
"useTheme",
|
|
3171
|
+
"useNavigationContainerRef",
|
|
3172
|
+
"useRouter",
|
|
3173
|
+
"router",
|
|
3174
|
+
"useLocalSearchParams",
|
|
3175
|
+
"useGlobalSearchParams",
|
|
3176
|
+
"usePathname",
|
|
3177
|
+
"useSegments",
|
|
3178
|
+
"useRootNavigationState",
|
|
3179
|
+
"Link",
|
|
3180
|
+
"Redirect",
|
|
3181
|
+
"Stack",
|
|
3182
|
+
"Tabs",
|
|
3183
|
+
"Drawer",
|
|
3184
|
+
"Slot",
|
|
3185
|
+
"SplashScreen",
|
|
3186
|
+
"SafeAreaProvider",
|
|
3187
|
+
"SafeAreaView",
|
|
3188
|
+
"useSafeAreaInsets",
|
|
3189
|
+
"useSafeAreaFrame",
|
|
3190
|
+
"SafeAreaInsetsContext",
|
|
3191
|
+
"SafeAreaFrameContext",
|
|
3192
|
+
"withSafeAreaInsets",
|
|
3193
|
+
"initialWindowMetrics",
|
|
3194
|
+
"StatusBar",
|
|
3195
|
+
"createVectorIconComponent",
|
|
3196
|
+
"Ionicons",
|
|
3197
|
+
"Feather",
|
|
3198
|
+
"MaterialIcons",
|
|
3199
|
+
"MaterialCommunityIcons",
|
|
3200
|
+
"FontAwesome",
|
|
3201
|
+
"FontAwesome5",
|
|
3202
|
+
"FontAwesome6",
|
|
3203
|
+
"AntDesign",
|
|
3204
|
+
"Entypo",
|
|
3205
|
+
"EvilIcons",
|
|
3206
|
+
"Foundation",
|
|
3207
|
+
"Octicons",
|
|
3208
|
+
"SimpleLineIcons",
|
|
3209
|
+
"Zocial",
|
|
3210
|
+
"Fontisto",
|
|
3211
|
+
"createIconSet",
|
|
3212
|
+
"createIconSetFromIcoMoon",
|
|
3213
|
+
"createIconSetFromFontello",
|
|
3214
|
+
"LinearGradient",
|
|
3215
|
+
"BlurView",
|
|
3216
|
+
"Image",
|
|
3217
|
+
"ImageBackground",
|
|
3218
|
+
"FlashList",
|
|
3219
|
+
"Svg",
|
|
3220
|
+
"Circle",
|
|
3221
|
+
"Ellipse",
|
|
3222
|
+
"G",
|
|
3223
|
+
"SvgText",
|
|
3224
|
+
"TSpan",
|
|
3225
|
+
"TextPath",
|
|
3226
|
+
"Path",
|
|
3227
|
+
"Polygon",
|
|
3228
|
+
"Polyline",
|
|
3229
|
+
"Line",
|
|
3230
|
+
"Rect",
|
|
3231
|
+
"Use",
|
|
3232
|
+
"SvgImage",
|
|
3233
|
+
"Symbol",
|
|
3234
|
+
"Defs",
|
|
3235
|
+
"RadialGradient",
|
|
3236
|
+
"Stop",
|
|
3237
|
+
"ClipPath",
|
|
3238
|
+
"Pattern",
|
|
3239
|
+
"Mask",
|
|
3240
|
+
"ForeignObject",
|
|
3241
|
+
"SvgXml",
|
|
3242
|
+
"SvgUri",
|
|
3243
|
+
"FadeIn",
|
|
3244
|
+
"FadeInDown",
|
|
3245
|
+
"FadeInUp",
|
|
3246
|
+
"FadeInLeft",
|
|
3247
|
+
"FadeInRight",
|
|
3248
|
+
"FadeOut",
|
|
3249
|
+
"FadeOutDown",
|
|
3250
|
+
"FadeOutUp",
|
|
3251
|
+
"SlideInDown",
|
|
3252
|
+
"SlideInUp",
|
|
3253
|
+
"SlideInLeft",
|
|
3254
|
+
"SlideInRight",
|
|
3255
|
+
"SlideOutDown",
|
|
3256
|
+
"SlideOutUp",
|
|
3257
|
+
"SlideOutLeft",
|
|
3258
|
+
"SlideOutRight",
|
|
3259
|
+
"ZoomIn",
|
|
3260
|
+
"ZoomOut",
|
|
3261
|
+
"Layout",
|
|
3262
|
+
"LinearTransition",
|
|
3263
|
+
"SequencedTransition",
|
|
3264
|
+
"FadingTransition",
|
|
3265
|
+
"useSharedValue",
|
|
3266
|
+
"useAnimatedStyle",
|
|
3267
|
+
"useDerivedValue",
|
|
3268
|
+
"useAnimatedProps",
|
|
3269
|
+
"useAnimatedScrollHandler",
|
|
3270
|
+
"useAnimatedRef",
|
|
3271
|
+
"useAnimatedGestureHandler",
|
|
3272
|
+
"withTiming",
|
|
3273
|
+
"withSpring",
|
|
3274
|
+
"withDelay",
|
|
3275
|
+
"withSequence",
|
|
3276
|
+
"withRepeat",
|
|
3277
|
+
"cancelAnimation",
|
|
3278
|
+
"runOnJS",
|
|
3279
|
+
"runOnUI",
|
|
3280
|
+
"interpolate",
|
|
3281
|
+
"Extrapolation",
|
|
3282
|
+
"Extrapolate",
|
|
3283
|
+
"Easing",
|
|
3284
|
+
"createAnimatedComponent",
|
|
3285
|
+
"Animated",
|
|
3286
|
+
"GestureHandlerRootView",
|
|
3287
|
+
"ScrollView",
|
|
3288
|
+
"FlatList",
|
|
3289
|
+
"TouchableOpacity",
|
|
3290
|
+
"TouchableHighlight",
|
|
3291
|
+
"TouchableWithoutFeedback",
|
|
3292
|
+
"Pressable",
|
|
3293
|
+
"Swipeable",
|
|
3294
|
+
"DrawerLayout",
|
|
3295
|
+
"PanGestureHandler",
|
|
3296
|
+
"TapGestureHandler",
|
|
3297
|
+
"LongPressGestureHandler",
|
|
3298
|
+
"GestureDetector",
|
|
3299
|
+
"Gesture",
|
|
3300
|
+
"State",
|
|
3301
|
+
"Directions",
|
|
3302
|
+
"ImpactFeedbackStyle",
|
|
3303
|
+
"NotificationFeedbackType",
|
|
3304
|
+
"impactAsync",
|
|
3305
|
+
"notificationAsync",
|
|
3306
|
+
"selectionAsync",
|
|
3307
|
+
"Constants",
|
|
3308
|
+
"useFonts",
|
|
3309
|
+
"loadAsync",
|
|
3310
|
+
"isLoaded",
|
|
3311
|
+
"preventAutoHideAsync",
|
|
3312
|
+
"hideAsync",
|
|
3313
|
+
"AsyncStorage",
|
|
3314
|
+
"getItemAsync",
|
|
3315
|
+
"setItemAsync",
|
|
3316
|
+
"deleteItemAsync",
|
|
3317
|
+
"setStringAsync",
|
|
3318
|
+
"getStringAsync",
|
|
3319
|
+
"openURL",
|
|
3320
|
+
"createURL",
|
|
3321
|
+
"create",
|
|
3322
|
+
"useStore",
|
|
3323
|
+
"QueryClient",
|
|
3324
|
+
"QueryClientProvider",
|
|
3325
|
+
"useQueryClient",
|
|
3326
|
+
"useQuery",
|
|
3327
|
+
"useInfiniteQuery",
|
|
3328
|
+
"useMutation",
|
|
3329
|
+
"createSafeChainableProxy",
|
|
3330
|
+
"SAFE_CHAINABLE_PROXY",
|
|
3331
|
+
"z",
|
|
3332
|
+
"string",
|
|
3333
|
+
"number",
|
|
3334
|
+
"boolean",
|
|
3335
|
+
"object",
|
|
3336
|
+
"array",
|
|
3337
|
+
"union",
|
|
3338
|
+
"literal",
|
|
3339
|
+
"record",
|
|
3340
|
+
"tuple",
|
|
3341
|
+
"any",
|
|
3342
|
+
"unknown",
|
|
3343
|
+
"optional",
|
|
3344
|
+
"nullable",
|
|
3345
|
+
"coerce",
|
|
3346
|
+
"zodResolver",
|
|
3347
|
+
"yupResolver",
|
|
3348
|
+
"scale",
|
|
3349
|
+
"verticalScale",
|
|
3350
|
+
"moderateScale",
|
|
3351
|
+
"moderateVerticalScale",
|
|
3352
|
+
"s",
|
|
3353
|
+
"vs",
|
|
3354
|
+
"ms",
|
|
3355
|
+
"mvs",
|
|
3356
|
+
"RFValue",
|
|
3357
|
+
"RFPercentage",
|
|
3358
|
+
"widthPercentageToDP",
|
|
3359
|
+
"heightPercentageToDP",
|
|
3360
|
+
"wp",
|
|
3361
|
+
"hp",
|
|
3362
|
+
"clsx",
|
|
3363
|
+
"cn",
|
|
3364
|
+
"twMerge",
|
|
3365
|
+
"styled",
|
|
3366
|
+
"cssInterop",
|
|
3367
|
+
"remapProps",
|
|
3368
|
+
"tw",
|
|
3369
|
+
"createTw",
|
|
3370
|
+
"useForm",
|
|
3371
|
+
"useFormContext",
|
|
3372
|
+
"FormProvider",
|
|
3373
|
+
"useWatch",
|
|
3374
|
+
"useFieldArray",
|
|
3375
|
+
"Controller",
|
|
3376
|
+
"SAFE_CONTEXT_FALLBACK",
|
|
3377
|
+
"createClient"
|
|
3378
|
+
];
|
|
3379
|
+
function safeRealpath(targetPath) {
|
|
3380
|
+
try {
|
|
3381
|
+
return fs$1.realpathSync(targetPath);
|
|
3382
|
+
} catch {
|
|
3383
|
+
return path.resolve(targetPath);
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
function sendJson(res, status, payload) {
|
|
3387
|
+
res.statusCode = status;
|
|
3388
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
3389
|
+
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
3390
|
+
res.end(JSON.stringify(payload));
|
|
3391
|
+
}
|
|
3392
|
+
function sendJs(res, code) {
|
|
3393
|
+
res.statusCode = 200;
|
|
3394
|
+
res.setHeader("Content-Type", "application/javascript; charset=utf-8");
|
|
3395
|
+
res.setHeader("Cache-Control", "no-store");
|
|
3396
|
+
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
3397
|
+
res.end(code);
|
|
3398
|
+
}
|
|
3399
|
+
function buildReactShimModule() {
|
|
3400
|
+
const lines = [
|
|
3401
|
+
`const R = window.__ZELUNE_RUNTIME__.React;`,
|
|
3402
|
+
`export default R;`,
|
|
3403
|
+
`export const jsx = R.createElement;`,
|
|
3404
|
+
`export const jsxs = R.createElement;`,
|
|
3405
|
+
`export const jsxDEV = R.createElement;`
|
|
3406
|
+
];
|
|
3407
|
+
for (const name of REACT_EXPORTS) {
|
|
3408
|
+
lines.push(`export const ${name} = R.${name};`);
|
|
3409
|
+
}
|
|
3410
|
+
return lines.join("\n");
|
|
3411
|
+
}
|
|
3412
|
+
function buildReactDomShimModule() {
|
|
3413
|
+
return [
|
|
3414
|
+
`const RD = window.__ZELUNE_RUNTIME__.ReactDOM;`,
|
|
3415
|
+
`export default RD;`,
|
|
3416
|
+
`export const createRoot = RD.createRoot;`,
|
|
3417
|
+
`export const hydrateRoot = RD.hydrateRoot;`,
|
|
3418
|
+
`export const flushSync = RD.flushSync;`,
|
|
3419
|
+
`export const createPortal = RD.createPortal;`
|
|
3420
|
+
].join("\n");
|
|
3421
|
+
}
|
|
3422
|
+
function buildReactNativeShimModule() {
|
|
3423
|
+
const lines = [
|
|
3424
|
+
`const RN = window.__ZELUNE_RUNTIME__.ReactNative;`,
|
|
3425
|
+
`export default RN.default || RN;`
|
|
3426
|
+
];
|
|
3427
|
+
for (const name of REACT_NATIVE_EXPORTS) {
|
|
3428
|
+
lines.push(`export const ${name} = RN.${name};`);
|
|
3429
|
+
}
|
|
3430
|
+
return lines.join("\n");
|
|
3431
|
+
}
|
|
3432
|
+
function buildLucideShimModule(requestedNames) {
|
|
3433
|
+
const lines = [
|
|
3434
|
+
`const L = window.__ZELUNE_RUNTIME__.LucideIcons || {};`,
|
|
3435
|
+
`const M = window.__ZELUNE_RUNTIME__.MockNav || {};`,
|
|
3436
|
+
`export default L;`
|
|
3437
|
+
];
|
|
3438
|
+
const unique = Array.from(
|
|
3439
|
+
new Set(
|
|
3440
|
+
requestedNames.map((s) => s.trim()).filter((s) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(s) && s !== "default")
|
|
3441
|
+
)
|
|
3442
|
+
);
|
|
3443
|
+
for (const name of unique) {
|
|
3444
|
+
lines.push(
|
|
3445
|
+
`export const ${name} = L[${JSON.stringify(name)}] || M.createVectorIconComponent(${JSON.stringify(name)});`
|
|
3446
|
+
);
|
|
3447
|
+
}
|
|
3448
|
+
return lines.join("\n");
|
|
3449
|
+
}
|
|
3450
|
+
function buildUniversalStubModule(requestedNames) {
|
|
3451
|
+
const lines = [
|
|
3452
|
+
`const React = window.__ZELUNE_RUNTIME__.React;`,
|
|
3453
|
+
`const RN = window.__ZELUNE_RUNTIME__.ReactNative;`,
|
|
3454
|
+
`const M = window.__ZELUNE_RUNTIME__.MockNav;`,
|
|
3455
|
+
`export default M.default || M;`,
|
|
3456
|
+
`const COMPOUND_SUB_NAMES = ['Item', 'Header', 'Footer', 'Title', 'Content', 'Trigger', 'Portal', 'Overlay', 'Close', 'Action', 'Cancel', 'Description', 'Label', 'Group', 'Separator', 'Value', 'Icon', 'Viewport', 'Root', 'Screen', 'Navigator', 'Provider', 'Consumer'];`,
|
|
3457
|
+
`function createSubStub(displayName) {`,
|
|
3458
|
+
` const Sub = React.forwardRef(function SubComp(props, ref) {`,
|
|
3459
|
+
` const content = typeof props?.children === 'function' ? props.children(M.SAFE_CHAINABLE_PROXY) : props?.children;`,
|
|
3460
|
+
` return React.createElement(RN.View, { style: props?.style, className: props?.className, ref }, content);`,
|
|
3461
|
+
` });`,
|
|
3462
|
+
` Sub.displayName = displayName;`,
|
|
3463
|
+
` return Sub;`,
|
|
3464
|
+
`}`,
|
|
3465
|
+
`function createComponentStub(displayName) {`,
|
|
3466
|
+
` const Comp = React.forwardRef(function StubComp(props, ref) {`,
|
|
3467
|
+
` if (typeof props?.render === 'function') {`,
|
|
3468
|
+
` return props.render({`,
|
|
3469
|
+
` field: { name: props?.name ?? '', value: props?.defaultValue ?? '', onChange: () => undefined, onBlur: () => undefined, ref: () => undefined },`,
|
|
3470
|
+
` fieldState: { invalid: false, isTouched: false, isDirty: false, error: undefined },`,
|
|
3471
|
+
` formState: { errors: {}, isSubmitting: false, isValid: true },`,
|
|
3472
|
+
` });`,
|
|
3473
|
+
` }`,
|
|
3474
|
+
` const content = typeof props?.children === 'function' ? props.children(M.SAFE_CHAINABLE_PROXY) : props?.children;`,
|
|
3475
|
+
` return React.createElement(RN.View, { style: props?.style, className: props?.className, ref }, content);`,
|
|
3476
|
+
` });`,
|
|
3477
|
+
` Comp.displayName = displayName;`,
|
|
3478
|
+
` for (const sub of COMPOUND_SUB_NAMES) {`,
|
|
3479
|
+
` Comp[sub] = createSubStub(displayName + '.' + sub);`,
|
|
3480
|
+
` }`,
|
|
3481
|
+
` return Comp;`,
|
|
3482
|
+
`}`,
|
|
3483
|
+
`function resolveExport(name) {`,
|
|
3484
|
+
` if (name in M) return M[name];`,
|
|
3485
|
+
` if (M.default && name in M.default) return M.default[name];`,
|
|
3486
|
+
` if (/^use[A-Z]/.test(name)) return () => M.SAFE_CHAINABLE_PROXY;`,
|
|
3487
|
+
` if (/^[A-Z]/.test(name)) return createComponentStub(name);`,
|
|
3488
|
+
` return M.SAFE_CHAINABLE_PROXY;`,
|
|
3489
|
+
`}`
|
|
3490
|
+
];
|
|
3491
|
+
const unique = Array.from(
|
|
3492
|
+
new Set(
|
|
3493
|
+
[...DEFAULT_MOCK_NAV_EXPORTS, ...requestedNames].map((s) => s.trim()).filter((s) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(s) && s !== "default")
|
|
3494
|
+
)
|
|
3495
|
+
);
|
|
3496
|
+
for (const name of unique) {
|
|
3497
|
+
lines.push(`export const ${name} = resolveExport(${JSON.stringify(name)});`);
|
|
3498
|
+
}
|
|
3499
|
+
return lines.join("\n");
|
|
3500
|
+
}
|
|
3501
|
+
async function resolveConnectedProjectName(workspaceRoot) {
|
|
3502
|
+
var _a;
|
|
3503
|
+
const activeRoot = getActiveProjectRoot(workspaceRoot);
|
|
3504
|
+
const dirName = path.basename(activeRoot);
|
|
3505
|
+
try {
|
|
3506
|
+
const appJsonRaw = await fs.readFile(path.join(activeRoot, "app.json"), "utf-8");
|
|
3507
|
+
const appJson = JSON.parse(appJsonRaw);
|
|
3508
|
+
if ((_a = appJson == null ? void 0 : appJson.expo) == null ? void 0 : _a.name) return appJson.expo.name;
|
|
3509
|
+
if (appJson == null ? void 0 : appJson.name) return appJson.name;
|
|
3510
|
+
} catch {
|
|
3511
|
+
}
|
|
3512
|
+
try {
|
|
3513
|
+
const pkgRaw = await fs.readFile(path.join(activeRoot, "package.json"), "utf-8");
|
|
3514
|
+
const pkg = JSON.parse(pkgRaw);
|
|
3515
|
+
if ((pkg == null ? void 0 : pkg.name) && pkg.name.toLowerCase() !== "zelune") {
|
|
3516
|
+
return pkg.name;
|
|
3517
|
+
}
|
|
3518
|
+
} catch {
|
|
3519
|
+
}
|
|
3520
|
+
return dirName;
|
|
3521
|
+
}
|
|
3522
|
+
async function startStudioHttpServer(options) {
|
|
3523
|
+
const { packageRoot, targetProjectRoot, workspaceRoot } = options;
|
|
3524
|
+
const distDir = path.resolve(packageRoot, "dist");
|
|
3525
|
+
const activeProjectRoot = getActiveProjectRoot(targetProjectRoot);
|
|
3526
|
+
const allowedRoots = Array.from(
|
|
3527
|
+
/* @__PURE__ */ new Set([
|
|
3528
|
+
path.resolve(packageRoot),
|
|
3529
|
+
safeRealpath(packageRoot),
|
|
3530
|
+
path.resolve(targetProjectRoot),
|
|
3531
|
+
safeRealpath(targetProjectRoot),
|
|
3532
|
+
path.resolve(activeProjectRoot),
|
|
3533
|
+
safeRealpath(activeProjectRoot),
|
|
3534
|
+
path.resolve(workspaceRoot),
|
|
3535
|
+
safeRealpath(workspaceRoot)
|
|
3536
|
+
])
|
|
3537
|
+
);
|
|
3538
|
+
function resolveCandidateFileParam(rawParam) {
|
|
3539
|
+
const clean = rawParam.split("?")[0].trim();
|
|
3540
|
+
if (!clean) return null;
|
|
3541
|
+
const candidate = path.isAbsolute(clean) ? path.resolve(clean) : path.resolve(getActiveProjectRoot(targetProjectRoot), clean.replace(/^[/\\]+/, ""));
|
|
3542
|
+
const lexicallyAllowed = allowedRoots.some(
|
|
3543
|
+
(root) => candidate === root || candidate.startsWith(root + path.sep)
|
|
3544
|
+
);
|
|
3545
|
+
if (!lexicallyAllowed || !fs$1.existsSync(candidate)) {
|
|
3546
|
+
return null;
|
|
3547
|
+
}
|
|
3548
|
+
const real = safeRealpath(candidate);
|
|
3549
|
+
const canonicallyAllowed = allowedRoots.some(
|
|
3550
|
+
(root) => real === root || real.startsWith(root + path.sep)
|
|
3551
|
+
);
|
|
3552
|
+
if (!canonicallyAllowed) {
|
|
3553
|
+
return null;
|
|
3554
|
+
}
|
|
3555
|
+
return real;
|
|
3556
|
+
}
|
|
3557
|
+
const requestHandler = async (req, res) => {
|
|
3558
|
+
const rawUrl = req.url ?? "/";
|
|
3559
|
+
const qIdx = rawUrl.indexOf("?");
|
|
3560
|
+
const pathname = qIdx === -1 ? rawUrl : rawUrl.slice(0, qIdx);
|
|
3561
|
+
const searchParams = new URLSearchParams(qIdx === -1 ? "" : rawUrl.slice(qIdx + 1));
|
|
3562
|
+
if (pathname.startsWith("/api/")) {
|
|
3563
|
+
const securityCheck = verifyApiRequestSecurity(req);
|
|
3564
|
+
if (!securityCheck.allowed) {
|
|
3565
|
+
return sendJson(res, 403, { ok: false, error: securityCheck.reason });
|
|
3566
|
+
}
|
|
3567
|
+
const activeRoot = getActiveProjectRoot(targetProjectRoot);
|
|
3568
|
+
const layoutFilePath = path.resolve(activeRoot, ".canvas-layout.json");
|
|
3569
|
+
try {
|
|
3570
|
+
if (req.method === "GET" && pathname === "/api/screens") {
|
|
3571
|
+
const [screens, projectName] = await Promise.all([
|
|
3572
|
+
discoverScreens(targetProjectRoot),
|
|
3573
|
+
resolveConnectedProjectName(targetProjectRoot)
|
|
3574
|
+
]);
|
|
3575
|
+
const history = getHistoryStatus();
|
|
3576
|
+
return sendJson(res, 200, { ok: true, projectName, screens, history });
|
|
3577
|
+
}
|
|
3578
|
+
if (req.method === "GET" && pathname === "/api/inspect") {
|
|
3579
|
+
const file = searchParams.get("file") ?? "";
|
|
3580
|
+
const astPath = searchParams.get("astPath") ?? "";
|
|
3581
|
+
const lineParam = searchParams.get("line");
|
|
3582
|
+
const line = lineParam ? Number.parseInt(lineParam, 10) : void 0;
|
|
3583
|
+
const element = await inspectElement(targetProjectRoot, { file, astPath, line });
|
|
3584
|
+
const history = getHistoryStatus();
|
|
3585
|
+
return sendJson(res, 200, { ok: true, element, history });
|
|
3586
|
+
}
|
|
3587
|
+
if (req.method === "POST" && pathname === "/api/patch") {
|
|
3588
|
+
const body = await readJsonBody(req);
|
|
3589
|
+
const result = await patchElement(targetProjectRoot, body);
|
|
3590
|
+
return sendJson(res, 200, {
|
|
3591
|
+
ok: true,
|
|
3592
|
+
element: result.element,
|
|
3593
|
+
history: {
|
|
3594
|
+
undoAvailable: result.undoAvailable,
|
|
3595
|
+
redoAvailable: result.redoAvailable
|
|
3596
|
+
}
|
|
3597
|
+
});
|
|
3598
|
+
}
|
|
3599
|
+
if (req.method === "POST" && pathname === "/api/reorder") {
|
|
3600
|
+
const body = await readJsonBody(req);
|
|
3601
|
+
const result = await reorderElement(targetProjectRoot, body);
|
|
3602
|
+
return sendJson(res, 200, {
|
|
3603
|
+
ok: true,
|
|
3604
|
+
element: result.element,
|
|
3605
|
+
history: {
|
|
3606
|
+
undoAvailable: result.undoAvailable,
|
|
3607
|
+
redoAvailable: result.redoAvailable
|
|
3608
|
+
}
|
|
3609
|
+
});
|
|
3610
|
+
}
|
|
3611
|
+
if (req.method === "POST" && pathname === "/api/insert") {
|
|
3612
|
+
const body = await readJsonBody(req);
|
|
3613
|
+
const result = await insertElement(targetProjectRoot, body);
|
|
3614
|
+
return sendJson(res, 200, {
|
|
3615
|
+
ok: true,
|
|
3616
|
+
element: result.element,
|
|
3617
|
+
history: {
|
|
3618
|
+
undoAvailable: result.undoAvailable,
|
|
3619
|
+
redoAvailable: result.redoAvailable
|
|
3620
|
+
}
|
|
3621
|
+
});
|
|
3622
|
+
}
|
|
3623
|
+
if (req.method === "POST" && pathname === "/api/duplicate") {
|
|
3624
|
+
const body = await readJsonBody(req);
|
|
3625
|
+
const result = await duplicateElement(targetProjectRoot, body);
|
|
3626
|
+
return sendJson(res, 200, {
|
|
3627
|
+
ok: true,
|
|
3628
|
+
element: result.element,
|
|
3629
|
+
history: {
|
|
3630
|
+
undoAvailable: result.undoAvailable,
|
|
3631
|
+
redoAvailable: result.redoAvailable
|
|
3632
|
+
}
|
|
3633
|
+
});
|
|
3634
|
+
}
|
|
3635
|
+
if (req.method === "POST" && pathname === "/api/delete") {
|
|
3636
|
+
const body = await readJsonBody(req);
|
|
3637
|
+
const result = await deleteElement(targetProjectRoot, body);
|
|
3638
|
+
return sendJson(res, 200, {
|
|
3639
|
+
ok: true,
|
|
3640
|
+
deletedAstPath: result.deletedAstPath,
|
|
3641
|
+
history: {
|
|
3642
|
+
undoAvailable: result.undoAvailable,
|
|
3643
|
+
redoAvailable: result.redoAvailable
|
|
3644
|
+
}
|
|
3645
|
+
});
|
|
3646
|
+
}
|
|
3647
|
+
if (req.method === "POST" && pathname === "/api/undo") {
|
|
3648
|
+
const result = await undoLastPatch(targetProjectRoot);
|
|
3649
|
+
return sendJson(res, 200, { ok: true, ...result });
|
|
3650
|
+
}
|
|
3651
|
+
if (req.method === "POST" && pathname === "/api/redo") {
|
|
3652
|
+
const result = await redoLastPatch(targetProjectRoot);
|
|
3653
|
+
return sendJson(res, 200, { ok: true, ...result });
|
|
3654
|
+
}
|
|
3655
|
+
if (req.method === "GET" && pathname === "/api/layout") {
|
|
3656
|
+
try {
|
|
3657
|
+
const raw = await fs.readFile(layoutFilePath, "utf-8");
|
|
3658
|
+
const validated = sanitizeLayoutPayload(JSON.parse(raw));
|
|
3659
|
+
return sendJson(res, 200, { ok: true, layout: validated });
|
|
3660
|
+
} catch {
|
|
3661
|
+
return sendJson(res, 200, { ok: true, layout: null });
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3664
|
+
if (req.method === "POST" && pathname === "/api/layout") {
|
|
3665
|
+
const body = await readJsonBody(req);
|
|
3666
|
+
const validated = sanitizeLayoutPayload(body);
|
|
3667
|
+
await fs.writeFile(layoutFilePath, JSON.stringify(validated, null, 2), "utf-8");
|
|
3668
|
+
return sendJson(res, 200, { ok: true });
|
|
3669
|
+
}
|
|
3670
|
+
return sendJson(res, 404, { ok: false, error: `Unknown API route: ${pathname}` });
|
|
3671
|
+
} catch (err) {
|
|
3672
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3673
|
+
return sendJson(res, 400, { ok: false, error: message });
|
|
3674
|
+
}
|
|
3675
|
+
}
|
|
3676
|
+
if (pathname === "/@zelune/shim/react.js") {
|
|
3677
|
+
return sendJs(res, buildReactShimModule());
|
|
3678
|
+
}
|
|
3679
|
+
if (pathname === "/@zelune/shim/react-dom.js") {
|
|
3680
|
+
return sendJs(res, buildReactDomShimModule());
|
|
3681
|
+
}
|
|
3682
|
+
if (pathname === "/@zelune/shim/react-native.js") {
|
|
3683
|
+
return sendJs(res, buildReactNativeShimModule());
|
|
3684
|
+
}
|
|
3685
|
+
if (pathname === "/@zelune/shim/lucide.js") {
|
|
3686
|
+
const exportsParam = searchParams.get("exports") ?? "";
|
|
3687
|
+
return sendJs(res, buildLucideShimModule(exportsParam.split(",")));
|
|
3688
|
+
}
|
|
3689
|
+
if (pathname === "/@zelune/shim/stub.js") {
|
|
3690
|
+
const exportsParam = searchParams.get("exports") ?? "";
|
|
3691
|
+
return sendJs(res, buildUniversalStubModule(exportsParam.split(",")));
|
|
3692
|
+
}
|
|
3693
|
+
if (pathname === "/@zelune/module") {
|
|
3694
|
+
const fileParam = searchParams.get("file") ?? "";
|
|
3695
|
+
const versionTag = searchParams.get("t") ?? void 0;
|
|
3696
|
+
const resolvedFile = resolveCandidateFileParam(fileParam);
|
|
3697
|
+
if (!resolvedFile) {
|
|
3698
|
+
return sendJs(res, "// Module not found\nexport default {};\n");
|
|
3699
|
+
}
|
|
3700
|
+
try {
|
|
3701
|
+
const compiled = compileModuleForBrowser({
|
|
3702
|
+
filePath: resolvedFile,
|
|
3703
|
+
targetProjectRoot: getActiveProjectRoot(targetProjectRoot),
|
|
3704
|
+
workspaceRoot,
|
|
3705
|
+
versionTag
|
|
3706
|
+
});
|
|
3707
|
+
return sendJs(res, compiled);
|
|
3708
|
+
} catch (err) {
|
|
3709
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3710
|
+
return sendJs(res, `throw new Error(${JSON.stringify(msg)});
|
|
3711
|
+
`);
|
|
3712
|
+
}
|
|
3713
|
+
}
|
|
3714
|
+
if (pathname === "/@zelune/asset") {
|
|
3715
|
+
const fileParam = searchParams.get("file") ?? "";
|
|
3716
|
+
const resolvedAsset = resolveCandidateFileParam(fileParam);
|
|
3717
|
+
if (!resolvedAsset) {
|
|
3718
|
+
res.statusCode = 404;
|
|
3719
|
+
return res.end("Not found");
|
|
3720
|
+
}
|
|
3721
|
+
const ext = path.extname(resolvedAsset).toLowerCase();
|
|
3722
|
+
res.statusCode = 200;
|
|
3723
|
+
res.setHeader("Content-Type", MIME_TYPES[ext] ?? "application/octet-stream");
|
|
3724
|
+
return fs$1.createReadStream(resolvedAsset).pipe(res);
|
|
3725
|
+
}
|
|
3726
|
+
if (pathname === "/" || pathname === "/index.html") {
|
|
3727
|
+
const indexHtmlPath = path.join(distDir, "index.html");
|
|
3728
|
+
if (!fs$1.existsSync(indexHtmlPath)) {
|
|
3729
|
+
res.statusCode = 500;
|
|
3730
|
+
return res.end("Zelune dist/index.html not found.");
|
|
3731
|
+
}
|
|
3732
|
+
const rawHtml = await fs.readFile(indexHtmlPath, "utf-8");
|
|
3733
|
+
const tailwindTheme = loadProjectTailwindTheme([targetProjectRoot, workspaceRoot]);
|
|
3734
|
+
const discoveredManifest = discoverScreenFilesSync(targetProjectRoot);
|
|
3735
|
+
const safeThemeJson = JSON.stringify(tailwindTheme).replace(/</g, "\\u003c");
|
|
3736
|
+
const safeManifestJson = JSON.stringify(discoveredManifest).replace(/</g, "\\u003c");
|
|
3737
|
+
const runtimeScript = `<script>window.__ZELUNE_STUDIO_SERVER__ = true; window.__ZELUNE_TAILWIND_THEME__ = ${safeThemeJson}; window.__ZELUNE_SCREEN_MANIFEST__ = ${safeManifestJson};<\/script>`;
|
|
3738
|
+
const injectedHtml = rawHtml.replace("<head>", `<head>
|
|
3739
|
+
${runtimeScript}`);
|
|
3740
|
+
res.statusCode = 200;
|
|
3741
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
3742
|
+
res.setHeader("Cache-Control", "no-store");
|
|
3743
|
+
return res.end(injectedHtml);
|
|
3744
|
+
}
|
|
3745
|
+
const relativeAsset = pathname.replace(/^\/+/, "");
|
|
3746
|
+
const assetCandidate = path.resolve(distDir, relativeAsset);
|
|
3747
|
+
if (assetCandidate.startsWith(distDir + path.sep) && fs$1.existsSync(assetCandidate) && fs$1.statSync(assetCandidate).isFile()) {
|
|
3748
|
+
const ext = path.extname(assetCandidate).toLowerCase();
|
|
3749
|
+
res.statusCode = 200;
|
|
3750
|
+
res.setHeader("Content-Type", MIME_TYPES[ext] ?? "application/octet-stream");
|
|
3751
|
+
return fs$1.createReadStream(assetCandidate).pipe(res);
|
|
3752
|
+
}
|
|
3753
|
+
res.statusCode = 404;
|
|
3754
|
+
res.end("Not found");
|
|
3755
|
+
};
|
|
3756
|
+
const basePort = options.port ?? 5173;
|
|
3757
|
+
for (let offset = 0; offset < 25; offset++) {
|
|
3758
|
+
const candidatePort = basePort + offset;
|
|
3759
|
+
try {
|
|
3760
|
+
const srv = await new Promise((resolve, reject) => {
|
|
3761
|
+
const s = http.createServer(requestHandler);
|
|
3762
|
+
s.once("error", reject);
|
|
3763
|
+
s.listen(candidatePort, "127.0.0.1", () => {
|
|
3764
|
+
s.removeListener("error", reject);
|
|
3765
|
+
resolve(s);
|
|
3766
|
+
});
|
|
3767
|
+
});
|
|
3768
|
+
return { server: srv, port: candidatePort };
|
|
3769
|
+
} catch (err) {
|
|
3770
|
+
const code = err == null ? void 0 : err.code;
|
|
3771
|
+
if (code === "EADDRINUSE" && !options.port) {
|
|
3772
|
+
continue;
|
|
3773
|
+
}
|
|
3774
|
+
throw err;
|
|
3775
|
+
}
|
|
3776
|
+
}
|
|
3777
|
+
throw new Error(`Unable to bind Studio HTTP server starting from port ${basePort}`);
|
|
3778
|
+
}
|
|
1906
3779
|
const __dirname$1 = path.dirname(fileURLToPath(import.meta.url));
|
|
1907
3780
|
const DEFAULT_WORKSPACE_ROOT = path.resolve(__dirname$1, "..");
|
|
1908
3781
|
const MCP_TOOLS = [
|
|
@@ -2155,7 +4028,7 @@ async function handleMcpJsonRpcRequest(request, customWorkspaceRoot) {
|
|
|
2155
4028
|
},
|
|
2156
4029
|
serverInfo: {
|
|
2157
4030
|
name: "zelune",
|
|
2158
|
-
version: "1.6.
|
|
4031
|
+
version: "1.6.5"
|
|
2159
4032
|
}
|
|
2160
4033
|
}
|
|
2161
4034
|
};
|
|
@@ -2254,7 +4127,7 @@ function startMcpServer(customWorkspaceRoot) {
|
|
|
2254
4127
|
}
|
|
2255
4128
|
const isDirectExecution = Boolean(process.argv[1]) && (() => {
|
|
2256
4129
|
try {
|
|
2257
|
-
return import.meta.url === pathToFileURL(
|
|
4130
|
+
return import.meta.url === pathToFileURL(fs$1.realpathSync(process.argv[1])).href;
|
|
2258
4131
|
} catch {
|
|
2259
4132
|
return false;
|
|
2260
4133
|
}
|
|
@@ -2265,5 +4138,6 @@ if (isDirectExecution) {
|
|
|
2265
4138
|
export {
|
|
2266
4139
|
MCP_TOOLS,
|
|
2267
4140
|
handleMcpJsonRpcRequest,
|
|
2268
|
-
startMcpServer
|
|
4141
|
+
startMcpServer,
|
|
4142
|
+
startStudioHttpServer
|
|
2269
4143
|
};
|