| #!/usr/bin/env node |
|
|
| const fs = require("fs"); |
| const path = require("path"); |
| const { execSync } = require("child_process"); |
|
|
| const cliDir = path.resolve(__dirname, ".."); |
| const appDir = path.resolve(cliDir, ".."); |
| const rootDir = path.resolve(appDir, ".."); |
| const cliAppDir = path.join(cliDir, "app"); |
| const buildHomeDir = path.join(cliDir, ".build-home"); |
| const buildDistDirName = ".next-cli-build"; |
| const buildDistDir = path.join(appDir, buildDistDirName); |
|
|
| |
| const EXCLUDE_PATTERNS = [ |
| "@img", |
| "sharp", |
| "detect-libc", |
| "logs", |
| ".env", |
| ".env.local", |
| ".env.*.local", |
| "*.log", |
| "tmp", |
| ".DS_Store", |
| ]; |
|
|
| function shouldExclude(name) { |
| return EXCLUDE_PATTERNS.some(pattern => { |
| if (pattern.includes("*")) { |
| const regex = new RegExp("^" + pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$"); |
| return regex.test(name); |
| } |
| return name === pattern; |
| }); |
| } |
|
|
| function copyRecursive(src, dest) { |
| if (!fs.existsSync(src)) { |
| console.warn(`Warning: Source ${src} does not exist`); |
| return; |
| } |
| |
| if (!fs.existsSync(dest)) { |
| fs.mkdirSync(dest, { recursive: true }); |
| } |
|
|
| const entries = fs.readdirSync(src, { withFileTypes: true }); |
| for (const entry of entries) { |
| if (shouldExclude(entry.name)) { |
| continue; |
| } |
|
|
| const srcPath = path.join(src, entry.name); |
| const destPath = path.join(dest, entry.name); |
|
|
| |
| try { |
| fs.accessSync(srcPath); |
| } catch { |
| continue; |
| } |
|
|
| if (entry.isDirectory()) { |
| copyRecursive(srcPath, destPath); |
| } else if (entry.isSymbolicLink()) { |
| |
| try { |
| const real = fs.realpathSync(srcPath); |
| if (fs.statSync(real).isDirectory()) { |
| copyRecursive(real, destPath); |
| } else { |
| fs.copyFileSync(real, destPath); |
| } |
| } catch {} |
| } else { |
| try { |
| fs.copyFileSync(srcPath, destPath); |
| } catch {} |
| } |
| } |
| } |
|
|
| console.log("π¦ Building 9Router CLI package with Next.js...\n"); |
|
|
| fs.mkdirSync(buildHomeDir, { recursive: true }); |
| fs.mkdirSync(path.join(buildHomeDir, "AppData", "Roaming"), { recursive: true }); |
| fs.mkdirSync(path.join(buildHomeDir, "AppData", "Local"), { recursive: true }); |
|
|
| |
| console.log("0οΈβ£ Syncing version to app/package.json..."); |
| const cliPkg = JSON.parse(fs.readFileSync(path.join(cliDir, "package.json"), "utf8")); |
| const appPkgPath = path.join(appDir, "package.json"); |
| const appPkg = JSON.parse(fs.readFileSync(appPkgPath, "utf8")); |
| if (appPkg.version !== cliPkg.version) { |
| appPkg.version = cliPkg.version; |
| fs.writeFileSync(appPkgPath, JSON.stringify(appPkg, null, 2) + "\n"); |
| console.log(`β
Version synced: ${cliPkg.version}\n`); |
| } else { |
| console.log(`β
Version already synced: ${cliPkg.version}\n`); |
| } |
|
|
| |
| console.log("1οΈβ£ Building Next.js app..."); |
| try { |
| execSync("npm run build", { |
| stdio: "inherit", |
| cwd: appDir, |
| env: { |
| ...process.env, |
| HOME: buildHomeDir, |
| USERPROFILE: buildHomeDir, |
| APPDATA: path.join(buildHomeDir, "AppData", "Roaming"), |
| LOCALAPPDATA: path.join(buildHomeDir, "AppData", "Local"), |
| NEXT_DIST_DIR: buildDistDirName, |
| NEXT_TRACING_ROOT_MODE: "workspace", |
| } |
| }); |
| console.log("β
Next.js build completed\n"); |
| } catch (error) { |
| console.error("β Next.js build failed"); |
| process.exit(1); |
| } |
|
|
| |
| console.log("2οΈβ£ Cleaning old app/cli/app..."); |
| if (fs.existsSync(cliAppDir)) { |
| fs.rmSync(cliAppDir, { recursive: true, force: true }); |
| } |
| console.log("β
Cleaned\n"); |
|
|
| |
| |
| |
| console.log("3οΈβ£ Copying Next.js standalone build to app/cli/app..."); |
| const standaloneRoot = path.join(appDir, ".next", "standalone"); |
| const standaloneRootResolved = path.join(buildDistDir, "standalone"); |
| const standaloneRootToUse = fs.existsSync(standaloneRootResolved) ? standaloneRootResolved : standaloneRoot; |
| const standaloneApp = fs.existsSync(path.join(standaloneRootToUse, "server.js")) |
| ? standaloneRootToUse |
| : path.join(standaloneRootToUse, "app"); |
| if (!fs.existsSync(standaloneApp)) { |
| console.error("β Next.js standalone build not found under .next/standalone"); |
| console.error("Expected either .next/standalone/server.js or .next/standalone/app/"); |
| process.exit(1); |
| } |
| copyRecursive(standaloneApp, cliAppDir); |
|
|
| |
| const standaloneNodeModules = path.join(standaloneRootToUse, "node_modules"); |
| if (standaloneApp !== standaloneRootToUse && fs.existsSync(standaloneNodeModules)) { |
| copyRecursive(standaloneNodeModules, path.join(cliAppDir, "node_modules")); |
| } |
| console.log("β
Copied standalone build\n"); |
|
|
| |
| const customServerSrc = path.join(appDir, "custom-server.js"); |
| if (fs.existsSync(customServerSrc)) { |
| fs.copyFileSync(customServerSrc, path.join(cliAppDir, "custom-server.js")); |
| console.log("β
Copied custom-server.js\n"); |
| } else { |
| console.warn("β οΈ custom-server.js not found β server will run without real-IP injection\n"); |
| } |
|
|
| |
| |
| |
| |
| console.log("3οΈβ£ b Configuring SQLite drivers..."); |
| function ensureModuleInBundle(pkg) { |
| const dest = path.join(cliAppDir, "node_modules", pkg); |
| if (fs.existsSync(dest)) { |
| console.log(`β
${pkg} already bundled`); |
| return; |
| } |
| const candidates = [ |
| path.join(appDir, "node_modules", pkg), |
| path.join(rootDir, "node_modules", pkg), |
| ]; |
| const src = candidates.find((p) => fs.existsSync(p)); |
| if (!src) { |
| console.warn(`β οΈ ${pkg} not found locally β bundle will rely on node:sqlite or runtime install`); |
| return; |
| } |
| fs.mkdirSync(path.dirname(dest), { recursive: true }); |
| copyRecursive(src, dest); |
| console.log(`β
Bundled ${pkg}`); |
| } |
| ensureModuleInBundle("sql.js"); |
| const betterDir = path.join(cliAppDir, "node_modules", "better-sqlite3"); |
| if (fs.existsSync(betterDir)) { |
| fs.rmSync(betterDir, { recursive: true, force: true }); |
| console.log("β
Stripped better-sqlite3 (lives in ~/.9router/runtime)"); |
| } |
| console.log(""); |
|
|
| |
| console.log("4οΈβ£ Copying static files..."); |
| const staticSrc = path.join(appDir, ".next", "static"); |
| const staticSrcResolved = path.join(buildDistDir, "static"); |
| const staticDest = path.join(cliAppDir, buildDistDirName, "static"); |
| if (fs.existsSync(staticSrcResolved) || fs.existsSync(staticSrc)) { |
| copyRecursive(fs.existsSync(staticSrcResolved) ? staticSrcResolved : staticSrc, staticDest); |
| console.log("β
Copied static files\n"); |
| } else { |
| console.log("βοΈ No static files found\n"); |
| } |
|
|
| |
| console.log("5οΈβ£ Copying public folder..."); |
| const publicSrc = path.join(appDir, "public"); |
| const publicDest = path.join(cliAppDir, "public"); |
| if (fs.existsSync(publicSrc)) { |
| copyRecursive(publicSrc, publicDest); |
| console.log("β
Copied public folder\n"); |
| } else { |
| console.log("βοΈ No public folder found\n"); |
| } |
|
|
| |
| console.log("6οΈβ£ Copying vendor-chunks..."); |
| const vendorChunksSrc = path.join(appDir, ".next", "server", "vendor-chunks"); |
| const vendorChunksSrcResolved = path.join(buildDistDir, "server", "vendor-chunks"); |
| const vendorChunksDest = path.join(cliAppDir, buildDistDirName, "server", "vendor-chunks"); |
| if (fs.existsSync(vendorChunksSrcResolved) || fs.existsSync(vendorChunksSrc)) { |
| copyRecursive(fs.existsSync(vendorChunksSrcResolved) ? vendorChunksSrcResolved : vendorChunksSrc, vendorChunksDest); |
| console.log("β
Copied vendor-chunks\n"); |
| } else { |
| console.log("βοΈ No vendor-chunks found\n"); |
| } |
|
|
| |
| console.log("7οΈβ£ Copying MITM server files..."); |
| const mitmSrc = path.join(appDir, "src", "mitm"); |
| const mitmDest = path.join(cliAppDir, "src", "mitm"); |
| if (fs.existsSync(mitmSrc)) { |
| copyRecursive(mitmSrc, mitmDest); |
| console.log("β
Copied MITM files\n"); |
| } else { |
| console.log("βοΈ No MITM files found\n"); |
| } |
|
|
| |
| console.log("7οΈβ£ b Copying updater files..."); |
| const updaterSrc = path.join(appDir, "src", "lib", "updater"); |
| const updaterDest = path.join(cliAppDir, "src", "lib", "updater"); |
| if (fs.existsSync(updaterSrc)) { |
| copyRecursive(updaterSrc, updaterDest); |
| console.log("β
Copied updater files\n"); |
| } else { |
| console.log("βοΈ No updater files found\n"); |
| } |
|
|
| |
| console.log("8οΈβ£ Building MITM server..."); |
| try { |
| execSync("node scripts/buildMitm.js", { stdio: "inherit", cwd: cliDir }); |
| console.log("β
MITM server build completed\n"); |
| } catch (error) { |
| console.error("β MITM build failed"); |
| process.exit(1); |
| } |
|
|
| console.log("β¨ CLI package build completed!"); |
| console.log(`π Output: ${cliAppDir}`); |
|
|
| try { |
| const { execSync: exec } = require("child_process"); |
| const size = exec(`du -sh "${cliAppDir}"`, { encoding: "utf8" }).trim(); |
| console.log(`π Package size: ${size.split("\t")[0]}`); |
| } catch (e) { |
| |
| } |
|
|