@jxrstudios/jxr 1.1.11 ā 1.2.13
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/bin/jxr.js +179 -4
- package/package.json +1 -1
package/bin/jxr.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { JXRServerManager, JXRDeployer } from "../src/index.ts";
|
|
3
3
|
|
|
4
|
-
import { mkdir, writeFile, cp } from "fs/promises";
|
|
4
|
+
import { mkdir, writeFile, cp, readdir } from "fs/promises";
|
|
5
5
|
import { existsSync } from "fs";
|
|
6
6
|
import path from "path";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
@@ -38,6 +38,11 @@ if (command === "init") {
|
|
|
38
38
|
},
|
|
39
39
|
dependencies: {
|
|
40
40
|
"@jxrstudios/jxr": "^1.0.5"
|
|
41
|
+
},
|
|
42
|
+
devDependencies: {
|
|
43
|
+
"@types/react": "^19.0.0",
|
|
44
|
+
"@types/react-dom": "^19.0.0",
|
|
45
|
+
"typescript": "^5.5.0"
|
|
41
46
|
}
|
|
42
47
|
};
|
|
43
48
|
await writeFile(
|
|
@@ -73,6 +78,175 @@ if (command === "init") {
|
|
|
73
78
|
process.exit(1);
|
|
74
79
|
}
|
|
75
80
|
|
|
81
|
+
} else if (command === "build") {
|
|
82
|
+
// Build command - production-optimized build
|
|
83
|
+
const platform = args.find((a) => a.startsWith("--platform="))?.split("=")[1] || "web";
|
|
84
|
+
const analyze = args.includes("--analyze");
|
|
85
|
+
const noMinify = args.includes("--no-minify");
|
|
86
|
+
const outDir = args.find((a) => a.startsWith("--out-dir="))?.split("=")[1] || "dist";
|
|
87
|
+
|
|
88
|
+
console.log(`šØ Building for ${platform}...`);
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const esbuild = await import("esbuild");
|
|
92
|
+
const fs = await import("fs");
|
|
93
|
+
const path = await import("path");
|
|
94
|
+
const crypto = await import("crypto");
|
|
95
|
+
|
|
96
|
+
// Ensure output directory exists
|
|
97
|
+
await mkdir(outDir, { recursive: true });
|
|
98
|
+
await mkdir(path.join(outDir, "assets"), { recursive: true });
|
|
99
|
+
|
|
100
|
+
// Find entry point
|
|
101
|
+
const entryFile = fs.existsSync("src/main.tsx") ? "src/main.tsx" :
|
|
102
|
+
fs.existsSync("src/main.ts") ? "src/main.ts" :
|
|
103
|
+
fs.existsSync("src/App.tsx") ? "src/App.tsx" : "src/index.tsx";
|
|
104
|
+
|
|
105
|
+
// Build configuration
|
|
106
|
+
const buildConfig = {
|
|
107
|
+
entryPoints: [entryFile],
|
|
108
|
+
bundle: true,
|
|
109
|
+
platform: platform === "node" ? "node" : "browser",
|
|
110
|
+
target: platform === "cloudflare-worker" ? "es2022" : "es2020",
|
|
111
|
+
format: "esm",
|
|
112
|
+
minify: !noMinify,
|
|
113
|
+
sourcemap: !noMinify,
|
|
114
|
+
splitting: platform !== "node",
|
|
115
|
+
outdir: path.join(outDir, "assets"),
|
|
116
|
+
entryNames: "[name]-[hash]",
|
|
117
|
+
chunkNames: "[name]-[hash]",
|
|
118
|
+
assetNames: "[name]-[hash]",
|
|
119
|
+
metafile: true,
|
|
120
|
+
define: {
|
|
121
|
+
"process.env.NODE_ENV": '"production"',
|
|
122
|
+
...(platform === "cloudflare-worker" && {
|
|
123
|
+
"process": "{}",
|
|
124
|
+
"process.env": "{}",
|
|
125
|
+
}),
|
|
126
|
+
},
|
|
127
|
+
external: [
|
|
128
|
+
"react", "react/jsx-runtime", "react/jsx-dev-runtime", "react-dom/client",
|
|
129
|
+
"wouter", "lucide-react", "sonner", "next-themes", "framer-motion", "motion-dom",
|
|
130
|
+
"@radix-ui/react-dialog", "@radix-ui/react-tooltip", "@radix-ui/react-slot",
|
|
131
|
+
"clsx", "tailwind-merge", "class-variance-authority",
|
|
132
|
+
"tailwindcss", "tw-animate-css",
|
|
133
|
+
...(platform === "cloudflare-worker" ? ["__STATIC_CONTENT_MANIFEST"] : []),
|
|
134
|
+
],
|
|
135
|
+
alias: {
|
|
136
|
+
"@": "./src",
|
|
137
|
+
},
|
|
138
|
+
loader: {
|
|
139
|
+
".tsx": "tsx",
|
|
140
|
+
".ts": "ts",
|
|
141
|
+
".css": "css",
|
|
142
|
+
".png": "file",
|
|
143
|
+
".jpg": "file",
|
|
144
|
+
".svg": "file",
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// Run build
|
|
149
|
+
const result = await esbuild.build(buildConfig);
|
|
150
|
+
|
|
151
|
+
console.log(`ā
Build complete: ${outDir}/`);
|
|
152
|
+
|
|
153
|
+
// Analyze bundle if requested
|
|
154
|
+
if (analyze && result.metafile) {
|
|
155
|
+
console.log("\nš Bundle Analysis:");
|
|
156
|
+
const outputs = Object.entries(result.metafile.outputs);
|
|
157
|
+
outputs.sort((a, b) => b[1].bytes - a[1].bytes);
|
|
158
|
+
outputs.slice(0, 10).forEach(([file, info]) => {
|
|
159
|
+
const sizeKB = (info.bytes / 1024).toFixed(2);
|
|
160
|
+
console.log(` ${file}: ${sizeKB} KB`);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Find main entry output (exclude source maps)
|
|
165
|
+
const mainOutput = Object.keys(result.metafile?.outputs || {}).find(k =>
|
|
166
|
+
(k.includes("main-") || k.includes("index-")) && k.endsWith(".js")
|
|
167
|
+
);
|
|
168
|
+
const vendorOutput = Object.keys(result.metafile?.outputs || {}).find(k =>
|
|
169
|
+
k.includes("chunk-") && k.endsWith(".js")
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
// Copy compiled CSS if available
|
|
173
|
+
if (fs.existsSync("src/index.compiled.css")) {
|
|
174
|
+
fs.copyFileSync("src/index.compiled.css", path.join(outDir, "assets", "index-[hash].css"));
|
|
175
|
+
console.log(` š Copied compiled CSS`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Find CSS output
|
|
179
|
+
const cssOutput = Object.keys(result.metafile?.outputs || {}).find(k => k.endsWith(".css"));
|
|
180
|
+
|
|
181
|
+
// Generate index.html with proper CSS and JS references
|
|
182
|
+
const indexHtml = `<!DOCTYPE html>
|
|
183
|
+
<html lang="en">
|
|
184
|
+
<head>
|
|
185
|
+
<meta charset="UTF-8">
|
|
186
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
187
|
+
<title>JXR.js ā Edge OS Runtime Framework</title>
|
|
188
|
+
<meta name="description" content="JXR.js is the next-generation edge runtime framework for React Native and React. MoQ transport, Web Crypto, Worker pools.">
|
|
189
|
+
|
|
190
|
+
<!-- Google Fonts -->
|
|
191
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
192
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
193
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
194
|
+
|
|
195
|
+
${cssOutput ? `<link rel="stylesheet" href="${cssOutput.replace(outDir, "").replace(/^\//, "")}">` : ""}
|
|
196
|
+
</head>
|
|
197
|
+
<body>
|
|
198
|
+
<div id="root"></div>
|
|
199
|
+
${vendorOutput ? `<script type="module" src="${vendorOutput.replace(outDir, "").replace(/^\//, "")}"></script>` : ""}
|
|
200
|
+
<script type="module" src="${mainOutput ? mainOutput.replace(outDir, "").replace(/^\//, "") : "assets/index.js"}"></script>
|
|
201
|
+
</body>
|
|
202
|
+
</html>`;
|
|
203
|
+
|
|
204
|
+
await writeFile(path.join(outDir, "index.html"), indexHtml);
|
|
205
|
+
|
|
206
|
+
// Generate crypto-signed manifest
|
|
207
|
+
const manifest = {
|
|
208
|
+
version: "1.0.0",
|
|
209
|
+
platform,
|
|
210
|
+
buildTime: new Date().toISOString(),
|
|
211
|
+
entries: {
|
|
212
|
+
main: mainOutput ? path.basename(mainOutput) : "index.js",
|
|
213
|
+
...(vendorOutput && { vendor: path.basename(vendorOutput) }),
|
|
214
|
+
},
|
|
215
|
+
files: Object.keys(result.metafile?.outputs || {}).map(k => path.basename(k)),
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// Sign manifest with ECDSA P-256
|
|
219
|
+
const manifestJson = JSON.stringify(manifest, null, 2);
|
|
220
|
+
const { privateKey, publicKey } = crypto.generateKeyPairSync("ec", {
|
|
221
|
+
namedCurve: "prime256v1",
|
|
222
|
+
});
|
|
223
|
+
const signature = crypto.sign("sha256", Buffer.from(manifestJson), privateKey);
|
|
224
|
+
|
|
225
|
+
const signedManifest = {
|
|
226
|
+
...manifest,
|
|
227
|
+
signature: signature.toString("base64"),
|
|
228
|
+
algorithm: "ECDSA-P256",
|
|
229
|
+
publicKey: publicKey.export({ type: "spki", format: "pem" }),
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
await writeFile(
|
|
233
|
+
path.join(outDir, "jxr-manifest.json"),
|
|
234
|
+
JSON.stringify(signedManifest, null, 2)
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
console.log(`ā
Manifest: ${outDir}/jxr-manifest.json`);
|
|
238
|
+
console.log(` Signed with ECDSA-P256`);
|
|
239
|
+
|
|
240
|
+
// Show output files
|
|
241
|
+
console.log("\nš Build outputs:");
|
|
242
|
+
const files = await readdir(outDir, { recursive: true });
|
|
243
|
+
files.forEach(f => console.log(` ${f}`));
|
|
244
|
+
|
|
245
|
+
} catch (err) {
|
|
246
|
+
console.error("ā Build failed:", err.message);
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
|
249
|
+
|
|
76
250
|
} else if (command === "deploy") {
|
|
77
251
|
// Deploy command
|
|
78
252
|
const projectPath = args[1] || "./dist";
|
|
@@ -123,8 +297,9 @@ if (command === "init") {
|
|
|
123
297
|
|
|
124
298
|
} else {
|
|
125
299
|
console.log("Usage:");
|
|
126
|
-
console.log(" jxr init <project-name>
|
|
127
|
-
console.log(" jxr dev [--port=3000]
|
|
128
|
-
console.log(" jxr
|
|
300
|
+
console.log(" jxr init <project-name> Create new project");
|
|
301
|
+
console.log(" jxr dev [--port=3000] Start dev server");
|
|
302
|
+
console.log(" jxr build [--platform=web] Production build");
|
|
303
|
+
console.log(" jxr deploy <path> [--env=prod] Deploy to production");
|
|
129
304
|
process.exit(1);
|
|
130
305
|
}
|