181 lines
5.1 KiB
TypeScript
181 lines
5.1 KiB
TypeScript
import { promises as fs } from "fs";
|
|
import * as path from "path";
|
|
|
|
import { transformEnvAccess } from "./env-scramble";
|
|
import {
|
|
generateBuildPassphrase,
|
|
generateBuildSalt,
|
|
generateFunctionName,
|
|
rewriteRuntimeDecoder,
|
|
RUNTIME_DECODER_PATH,
|
|
transformSource,
|
|
} from "./scramble-shared";
|
|
import { stripLogCalls } from "./strip-logs";
|
|
|
|
(globalThis as any).scramble = (s: string) => s;
|
|
|
|
const { StringScrambler } = await import("../src/utils/stringtool");
|
|
|
|
const PASSPHRASE = generateBuildPassphrase();
|
|
const SALT = generateBuildSalt();
|
|
const FN_NAME = generateFunctionName();
|
|
console.log(
|
|
`[BUILD-CLI] Generated build passphrase (${PASSPHRASE.length} chars)`,
|
|
);
|
|
console.log(`[BUILD-CLI] Generated build salt (${SALT.length} chars)`);
|
|
console.log(`[BUILD-CLI] Generated function name: ${FN_NAME}`);
|
|
|
|
const scrambler = new StringScrambler(PASSPHRASE, SALT);
|
|
|
|
const loggerSource = await fs.readFile("src/utils/logger.ts", "utf-8");
|
|
const isSilent = /const\s+isSilent\s*=\s*true/.test(loggerSource);
|
|
console.log(`[BUILD-CLI] isSilent = ${isSilent}`);
|
|
|
|
const CLI_DIR = "src/cli";
|
|
const OUT_DIR = "dist-cli";
|
|
|
|
const BEAUTIFY_IDENTITY = /^\(globalThis as Record<string, unknown>\)\.beautify\s*=\s*\(s:\s*string\):\s*string\s*=>\s*s;?\s*\n?/m;
|
|
const SCRAMBLE_IDENTITY = /^\(globalThis as Record<string, unknown>\)\.scramble\s*=\s*\(s:\s*string\):\s*string\s*=>\s*s;?\s*\n?/m;
|
|
|
|
const SHEBANG = /^#!.*\n/;
|
|
|
|
function stripCLIGlobals(code: string): string {
|
|
return code
|
|
.replace(SHEBANG, "")
|
|
.replace(SCRAMBLE_IDENTITY, "")
|
|
.replace(BEAUTIFY_IDENTITY, "");
|
|
}
|
|
|
|
async function transformFile(filePath: string): Promise<string> {
|
|
const code = await fs.readFile(filePath, "utf-8");
|
|
|
|
console.log(`[TRANSFORM] Processing: ${filePath}`);
|
|
|
|
const { code: envRewritten } = transformEnvAccess(
|
|
code,
|
|
"[TRANSFORM]",
|
|
filePath,
|
|
);
|
|
|
|
const { code: scrambled } = transformSource(
|
|
envRewritten,
|
|
scrambler,
|
|
FN_NAME,
|
|
"[TRANSFORM]",
|
|
filePath,
|
|
);
|
|
|
|
if (isSilent) {
|
|
const { code: stripped } = stripLogCalls(
|
|
scrambled,
|
|
"[TRANSFORM]",
|
|
filePath,
|
|
);
|
|
return stripped;
|
|
}
|
|
|
|
return scrambled;
|
|
}
|
|
|
|
async function walkDir(dir: string): Promise<string[]> {
|
|
const files: string[] = [];
|
|
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
|
|
for (const entry of entries) {
|
|
const fullPath = path.join(dir, entry.name);
|
|
if (entry.isDirectory()) {
|
|
files.push(...(await walkDir(fullPath)));
|
|
} else if (entry.name.endsWith(".ts") && !entry.name.endsWith(".d.ts")) {
|
|
files.push(fullPath);
|
|
}
|
|
}
|
|
|
|
return files;
|
|
}
|
|
|
|
async function buildCLI() {
|
|
const tempDir = "./.bun-temp-cli";
|
|
await fs.rm(tempDir, { recursive: true, force: true });
|
|
await fs.mkdir(tempDir, { recursive: true });
|
|
|
|
console.log("[BUILD-CLI] Copying and transforming source files...");
|
|
|
|
const files = await walkDir("./src");
|
|
console.log(`[BUILD-CLI] Processing ${files.length} TypeScript files`);
|
|
|
|
for (const file of files) {
|
|
let transformed = await transformFile(file);
|
|
|
|
// Strip identity scramble/beautify from CLI entrypoints — the built
|
|
// bundle uses runtimeDecoder instead.
|
|
if (file.startsWith(CLI_DIR + "/")) {
|
|
transformed = stripCLIGlobals(transformed);
|
|
}
|
|
|
|
const tempFile = path.join(tempDir, path.relative("./src", file));
|
|
await fs.mkdir(path.dirname(tempFile), { recursive: true });
|
|
await fs.writeFile(tempFile, transformed, "utf-8");
|
|
}
|
|
|
|
const tempDecoderPath = path.join(
|
|
tempDir,
|
|
path.relative("./src", path.resolve(RUNTIME_DECODER_PATH)),
|
|
);
|
|
const rewrittenDecoder = await rewriteRuntimeDecoder(
|
|
RUNTIME_DECODER_PATH,
|
|
PASSPHRASE,
|
|
SALT,
|
|
FN_NAME,
|
|
);
|
|
await fs.mkdir(path.dirname(tempDecoderPath), { recursive: true });
|
|
await fs.writeFile(tempDecoderPath, rewrittenDecoder, "utf-8");
|
|
console.log(
|
|
`[BUILD-CLI] Injected build passphrase into ${path.relative(tempDir, tempDecoderPath)}`,
|
|
);
|
|
|
|
const cliTempDir = path.join(tempDir, "cli");
|
|
const cliEntries = (await fs.readdir(cliTempDir))
|
|
.filter((f) => f.endsWith(".ts") && f !== "common.ts")
|
|
.map((f) => path.join(cliTempDir, f));
|
|
|
|
console.log(`[BUILD-CLI] Found ${cliEntries.length} CLI entrypoints`);
|
|
|
|
// Prepend runtimeDecoder import (relative from src/cli/ → src/utils/)
|
|
for (const ep of cliEntries) {
|
|
const epCode = await fs.readFile(ep, "utf-8");
|
|
await fs.writeFile(
|
|
ep,
|
|
`import "../utils/runtimeDecoder";\n${epCode}`,
|
|
"utf-8",
|
|
);
|
|
}
|
|
|
|
const allEntrypoints = [...cliEntries];
|
|
|
|
console.log(
|
|
`[BUILD-CLI] Building ${allEntrypoints.length} entrypoints...`,
|
|
);
|
|
|
|
await Bun.build({
|
|
entrypoints: allEntrypoints,
|
|
outdir: OUT_DIR,
|
|
root: tempDir,
|
|
naming: {
|
|
entry: "[dir]/[name].[ext]",
|
|
},
|
|
target: "bun",
|
|
minify: true,
|
|
});
|
|
|
|
console.log("[BUILD-CLI] Cleaning up temp directory...");
|
|
await fs.rm(tempDir, { recursive: true, force: true });
|
|
|
|
console.log("[BUILD-CLI] ✓ Build complete!");
|
|
console.log("[BUILD-CLI]");
|
|
for (const ep of cliEntries) {
|
|
const name = path.basename(ep, ".ts");
|
|
console.log(`[BUILD-CLI] dist-cli/cli/${name}.js`);
|
|
}
|
|
}
|
|
|
|
buildCLI().catch(console.error); |