first
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env bun
|
||||
import plugin from 'bun-plugin-tailwind';
|
||||
import { config as dotenv } from 'dotenv';
|
||||
import { existsSync } from 'fs';
|
||||
import { rm } from 'fs/promises';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { buildConfig } from './helpers';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const envPath = path.resolve(__dirname, '../../.env');
|
||||
dotenv({ path: envPath });
|
||||
|
||||
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
||||
console.log(`
|
||||
🏗️ Bun Build Script
|
||||
|
||||
Usage: bun run build.ts [options]
|
||||
|
||||
Common Options:
|
||||
--outdir <path> Output directory (default: "dist")
|
||||
--minify Enable minification (or --minify.whitespace, --minify.syntax, etc)
|
||||
--sourcemap <type> Sourcemap type: none|linked|inline|external
|
||||
--target <target> Build target: browser|bun|node
|
||||
--format <format> Output format: esm|cjs|iife
|
||||
--splitting Enable code splitting
|
||||
--packages <type> Package handling: bundle|external
|
||||
--public-path <path> Public path for assets
|
||||
--env <mode> Environment handling: inline|disable|prefix*
|
||||
--conditions <list> Package.json export conditions (comma separated)
|
||||
--external <list> External packages (comma separated)
|
||||
--banner <text> Add banner text to output
|
||||
--footer <text> Add footer text to output
|
||||
--define <obj> Define global constants (e.g. --define.VERSION=1.0.0)
|
||||
--help, -h Show this help message
|
||||
|
||||
Example:
|
||||
bun run build.ts --outdir=dist --minify --sourcemap=linked --external=react,react-dom
|
||||
`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const toCamelCase = (str: string): string => str.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());
|
||||
|
||||
const parseValue = (value: string): unknown => {
|
||||
if (value === 'true') return true;
|
||||
if (value === 'false') return false;
|
||||
|
||||
if (/^\d+$/.test(value)) return parseInt(value, 10);
|
||||
if (/^\d*\.\d+$/.test(value)) return parseFloat(value);
|
||||
|
||||
if (value.includes(',')) return value.split(',').map((v) => v.trim());
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
function parseArgs(): Partial<Bun.BuildConfig> {
|
||||
const config: Record<string, unknown> = {};
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
if (arg === undefined) continue;
|
||||
if (!arg.startsWith('--')) continue;
|
||||
|
||||
if (arg.startsWith('--no-')) {
|
||||
const key = toCamelCase(arg.slice(5));
|
||||
config[key] = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!arg.includes('=') && (i === args.length - 1 || args[i + 1]?.startsWith('--'))) {
|
||||
const key = toCamelCase(arg.slice(2));
|
||||
config[key] = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
let key: string;
|
||||
let value: string;
|
||||
|
||||
if (arg.includes('=')) {
|
||||
[key, value] = arg.slice(2).split('=', 2) as [string, string];
|
||||
} else {
|
||||
key = arg.slice(2);
|
||||
value = args[++i] ?? '';
|
||||
}
|
||||
|
||||
key = toCamelCase(key);
|
||||
|
||||
if (key.includes('.')) {
|
||||
const [parentKey, childKey] = key.split('.');
|
||||
if (parentKey && childKey) {
|
||||
config[parentKey] = config[parentKey] || {};
|
||||
(config[parentKey] as Record<string, unknown>)[childKey] = parseValue(value);
|
||||
}
|
||||
} else {
|
||||
config[key] = parseValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
return config as Partial<Bun.BuildConfig>;
|
||||
}
|
||||
|
||||
const formatFileSize = (bytes: number): string => {
|
||||
const units = ['B', 'KB', 'MB', 'GB'];
|
||||
let size = bytes;
|
||||
let unitIndex = 0;
|
||||
|
||||
while (size >= 1024 && unitIndex < units.length - 1) {
|
||||
size /= 1024;
|
||||
unitIndex++;
|
||||
}
|
||||
|
||||
return `${size.toFixed(2)} ${units[unitIndex]}`;
|
||||
};
|
||||
|
||||
console.log('\n🚀 Starting build process...\n');
|
||||
|
||||
const cliConfig = parseArgs();
|
||||
const outdir = cliConfig.outdir || path.join(process.cwd(), 'dist');
|
||||
|
||||
if (existsSync(outdir)) {
|
||||
console.log(`🗑️ Cleaning previous build at ${outdir}`);
|
||||
await rm(outdir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
const start = performance.now();
|
||||
|
||||
const configPath = 'src/workspaces/config/src/index.ts';
|
||||
if (existsSync(configPath)) {
|
||||
console.log('🔧 Generating config with environment values...');
|
||||
buildConfig(configPath, 'dashboard');
|
||||
}
|
||||
|
||||
const entrypoints = [...new Bun.Glob('**.html').scanSync('src/apps/dashboard')]
|
||||
.map((a) => path.resolve('src/apps/dashboard', a))
|
||||
.filter((dir) => !dir.includes('node_modules'));
|
||||
console.log(`📄 Found ${entrypoints.length} HTML ${entrypoints.length === 1 ? 'file' : 'files'} to process\n`);
|
||||
|
||||
const result = await Bun.build({
|
||||
entrypoints,
|
||||
outdir,
|
||||
plugins: [plugin],
|
||||
minify: true,
|
||||
target: 'browser',
|
||||
sourcemap: 'linked',
|
||||
define: {
|
||||
'process.env.NODE_ENV': JSON.stringify('production'),
|
||||
},
|
||||
...cliConfig,
|
||||
});
|
||||
|
||||
const end = performance.now();
|
||||
|
||||
const outputTable = result.outputs.map((output) => ({
|
||||
File: path.relative(process.cwd(), output.path),
|
||||
Type: output.kind,
|
||||
Size: formatFileSize(output.size),
|
||||
}));
|
||||
|
||||
console.table(outputTable);
|
||||
const buildTime = (end - start).toFixed(2);
|
||||
|
||||
console.log(`\n✅ Build completed in ${buildTime}ms\n`);
|
||||
@@ -0,0 +1,81 @@
|
||||
import { readFileSync, writeFileSync } from "fs";
|
||||
import { parse } from "dotenv";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
export function buildConfig(configPath: string, target: string): void {
|
||||
// Read the config file
|
||||
const configContent = readFileSync(configPath, "utf-8");
|
||||
|
||||
// Extract variable names from config (e.g., DASHBOARD_URL, API_URL, EXPERIMENTS_URL)
|
||||
// Match both single and double quotes, empty or populated strings
|
||||
const matches = configContent.match(/(\w+):\s*['"][^'"]*['"]/g) ?? [];
|
||||
const variableNames = Object.keys(
|
||||
matches.reduce((acc: Record<string, string>, match: string) => {
|
||||
const varName = match.split(":")[0]?.trim();
|
||||
if (varName) acc[varName] = "";
|
||||
return acc;
|
||||
}, {}),
|
||||
);
|
||||
|
||||
// Read root .env file
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const envPath = path.resolve(__dirname, "../../.env");
|
||||
const envContent = readFileSync(envPath, "utf-8");
|
||||
const envVars = parse(envContent);
|
||||
|
||||
// Map config variables to .env variables (check PUBLIC_, VITE_, BUN_PUBLIC_, or plain name)
|
||||
const mappedValues: Record<string, string> = {};
|
||||
for (const varName of variableNames) {
|
||||
const publicKey = `PUBLIC_${varName}`;
|
||||
const viteKey = `VITE_${varName}`;
|
||||
const bunKey = `BUN_PUBLIC_${varName}`;
|
||||
|
||||
mappedValues[varName] =
|
||||
envVars[publicKey] ||
|
||||
envVars[viteKey] ||
|
||||
envVars[bunKey] ||
|
||||
envVars[varName] ||
|
||||
"";
|
||||
}
|
||||
|
||||
// Apply runtime-specific config logic
|
||||
applyRuntimeSpecificConfig(target, mappedValues, envVars);
|
||||
|
||||
// Generate new config content - replace all values regardless of current state
|
||||
let newConfigContent = configContent;
|
||||
for (const [varName, value] of Object.entries(mappedValues)) {
|
||||
// Match varName: 'anything' or "anything" and replace with new value (preserve single quotes)
|
||||
const regex = new RegExp(`${varName}:\\s*['"][^'"]*['"]`, "g");
|
||||
newConfigContent = newConfigContent.replace(
|
||||
regex,
|
||||
`${varName}: '${value}'`,
|
||||
);
|
||||
}
|
||||
|
||||
// Write back to config file
|
||||
writeFileSync(configPath, newConfigContent, "utf-8");
|
||||
|
||||
console.log(`✅ Config file updated with environment values (${target})`);
|
||||
}
|
||||
|
||||
function applyRuntimeSpecificConfig(
|
||||
target: string,
|
||||
mappedValues: Record<string, string>,
|
||||
envVars: Record<string, string>,
|
||||
): void {
|
||||
switch (target) {
|
||||
case "core":
|
||||
// Core runtime specific config
|
||||
break;
|
||||
case "experiments":
|
||||
// Experiments runtime specific config
|
||||
break;
|
||||
case "tracking":
|
||||
// Tracking runtime specific config
|
||||
break;
|
||||
case "editorSetup":
|
||||
// Editor setup specific config
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env bun
|
||||
import plugin from 'bun-plugin-tailwind';
|
||||
import { config as dotenv } from 'dotenv';
|
||||
import { existsSync } from 'fs';
|
||||
import { rm } from 'fs/promises';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { buildConfig } from './helpers';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const envPath = path.resolve(__dirname, '../../.env');
|
||||
dotenv({ path: envPath });
|
||||
|
||||
const outdir = path.join(process.cwd(), 'build/landing');
|
||||
|
||||
if (existsSync(outdir)) {
|
||||
console.log(`🗑️ Cleaning previous build at ${outdir}`);
|
||||
await rm(outdir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
const start = performance.now();
|
||||
|
||||
const configPath = 'src/workspaces/config/src/index.ts';
|
||||
if (existsSync(configPath)) {
|
||||
console.log('🔧 Generating config with environment values...');
|
||||
buildConfig(configPath, 'landing');
|
||||
}
|
||||
|
||||
const entrypoints = [...new Bun.Glob('**.html').scanSync('src/apps/officer-web')]
|
||||
.map((a) => path.resolve('src/apps/officer-web', a))
|
||||
.filter((dir) => !dir.includes('node_modules'));
|
||||
console.log(`📄 Found ${entrypoints.length} HTML ${entrypoints.length === 1 ? 'file' : 'files'} to process\n`);
|
||||
|
||||
const formatFileSize = (bytes: number): string => {
|
||||
const units = ['B', 'KB', 'MB', 'GB'];
|
||||
let size = bytes;
|
||||
let unitIndex = 0;
|
||||
|
||||
while (size >= 1024 && unitIndex < units.length - 1) {
|
||||
size /= 1024;
|
||||
unitIndex++;
|
||||
}
|
||||
|
||||
return `${size.toFixed(2)} ${units[unitIndex]}`;
|
||||
};
|
||||
|
||||
const result = await Bun.build({
|
||||
entrypoints,
|
||||
outdir,
|
||||
plugins: [plugin],
|
||||
minify: true,
|
||||
target: 'browser',
|
||||
sourcemap: 'linked',
|
||||
define: {
|
||||
'process.env.NODE_ENV': JSON.stringify('production'),
|
||||
},
|
||||
});
|
||||
|
||||
const end = performance.now();
|
||||
|
||||
const outputTable = result.outputs.map((output) => ({
|
||||
File: path.relative(process.cwd(), output.path),
|
||||
Type: output.kind,
|
||||
Size: formatFileSize(output.size),
|
||||
}));
|
||||
|
||||
console.table(outputTable);
|
||||
const buildTime = (end - start).toFixed(2);
|
||||
|
||||
console.log(`\n✅ Build completed in ${buildTime}ms\n`);
|
||||
Executable
+212
@@ -0,0 +1,212 @@
|
||||
#!/usr/bin/env bun
|
||||
import { config as dotenv } from "dotenv";
|
||||
import { existsSync } from "fs";
|
||||
import { rm } from "fs/promises";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { $ } from "bun";
|
||||
import { buildConfig } from "./helpers";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const envPath = path.resolve(__dirname, "../../.env");
|
||||
dotenv({ path: envPath });
|
||||
|
||||
if (process.argv.includes("--help") || process.argv.includes("-h")) {
|
||||
console.log(`
|
||||
🏗️ Runtime Build Script
|
||||
|
||||
Usage: bun run scripts/build/runtime.ts [options]
|
||||
|
||||
Builds pertento-runtime to src/apps/runtime/dist
|
||||
|
||||
Options:
|
||||
--help, -h Show this help message
|
||||
--core Build only runtime-core (default)
|
||||
--experiments Build only runtime-experiments
|
||||
--tracking Build only runtime-tracking
|
||||
--editor-setup Build only editor-setup
|
||||
--all Build all runtimes
|
||||
|
||||
Example:
|
||||
bun run scripts/build/runtime.ts
|
||||
bun run scripts/build/runtime.ts --experiments
|
||||
bun run scripts/build/runtime.ts --tracking
|
||||
bun run scripts/build/runtime.ts --editor-setup
|
||||
bun run scripts/build/runtime.ts --all
|
||||
`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const formatFileSize = (bytes: number): string => {
|
||||
const units = ["B", "KB", "MB", "GB"];
|
||||
let size = bytes;
|
||||
let unitIndex = 0;
|
||||
|
||||
while (size >= 1024 && unitIndex < units.length - 1) {
|
||||
size /= 1024;
|
||||
unitIndex++;
|
||||
}
|
||||
|
||||
return `${size.toFixed(2)} ${units[unitIndex]}`;
|
||||
};
|
||||
|
||||
interface RuntimeBuildConfig {
|
||||
name: string;
|
||||
srcDir: string;
|
||||
configPath: string;
|
||||
outFilename: string;
|
||||
format: "iife" | "esm";
|
||||
entry?: string;
|
||||
}
|
||||
|
||||
const runtimeConfigs: Record<string, RuntimeBuildConfig> = {
|
||||
core: {
|
||||
name: "runtime-core",
|
||||
srcDir: "src/apps/runtime/runtime-core/src",
|
||||
configPath: "src/apps/runtime/runtime-core/src/config.ts",
|
||||
outFilename: "pertentoRuntime5.js",
|
||||
format: "iife",
|
||||
},
|
||||
experiments: {
|
||||
name: "runtime-experiments",
|
||||
srcDir: "src/apps/runtime/runtime-experiments/src",
|
||||
configPath: "src/apps/runtime/runtime-experiments/src/config.ts",
|
||||
outFilename: "pertentoRuntime5Experiments.js",
|
||||
format: "esm",
|
||||
},
|
||||
tracking: {
|
||||
name: "runtime-tracking",
|
||||
srcDir: "src/apps/runtime/runtime-tracking/src",
|
||||
configPath: "src/apps/runtime/runtime-tracking/src/config.ts",
|
||||
outFilename: "pertentoRuntime5Tracking.js",
|
||||
format: "iife",
|
||||
},
|
||||
editorSetup: {
|
||||
name: "editor-setup",
|
||||
srcDir: "src/apps/runtime/runtime-tracking/src",
|
||||
configPath: "src/apps/runtime/runtime-tracking/src/config.ts",
|
||||
outFilename: "pertentoEditorSetup.js",
|
||||
format: "iife",
|
||||
entry: "setup-for-editor-extension.ts",
|
||||
},
|
||||
};
|
||||
|
||||
let buildTargets: string[] = ["core"];
|
||||
if (process.argv.includes("--experiments")) {
|
||||
buildTargets = ["experiments"];
|
||||
} else if (process.argv.includes("--tracking")) {
|
||||
buildTargets = ["tracking"];
|
||||
} else if (process.argv.includes("--editor-setup")) {
|
||||
buildTargets = ["editorSetup"];
|
||||
} else if (process.argv.includes("--all")) {
|
||||
buildTargets = ["core", "experiments", "tracking", "editorSetup"];
|
||||
}
|
||||
|
||||
console.log("\n🚀 Starting runtime build process...\n");
|
||||
|
||||
const { BUILD_ENV } = process.env;
|
||||
const isProduction = BUILD_ENV === "production";
|
||||
console.log(
|
||||
`📋 Build environment: ${BUILD_ENV || "development"} ${isProduction ? "(minified)" : "(development)"}\n`,
|
||||
);
|
||||
|
||||
const outdir = "src/apps/runtime/dist";
|
||||
|
||||
if (existsSync(outdir)) {
|
||||
console.log(`🗑️ Cleaning previous build at ${outdir}`);
|
||||
await rm(outdir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
const start = performance.now();
|
||||
|
||||
async function buildRuntime(target: string) {
|
||||
const config = runtimeConfigs[target];
|
||||
if (!config) {
|
||||
console.error(`Unknown runtime target: ${target}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Generate config with environment values
|
||||
console.log(`🔧 Generating config for ${config.name}...\n`);
|
||||
buildConfig(config.configPath, target);
|
||||
|
||||
console.log(`📦 Building ${config.name}...\n`);
|
||||
|
||||
const entryFile = config.entry || "index.ts";
|
||||
const result = await Bun.build({
|
||||
entrypoints: [`${config.srcDir}/${entryFile}`],
|
||||
outdir,
|
||||
minify: isProduction,
|
||||
target: "browser",
|
||||
format: config.format,
|
||||
naming: config.outFilename,
|
||||
define: {
|
||||
"process.env.NODE_ENV": JSON.stringify(
|
||||
isProduction ? "production" : "development",
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
console.error(`Build failed for ${config.name}!`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Compress the output files using Bun shell
|
||||
const jsFile = path.join(outdir, config.outFilename);
|
||||
if (existsSync(jsFile)) {
|
||||
// Create gzip version
|
||||
await $`gzip -k -f ${jsFile}`;
|
||||
|
||||
// Create brotli version
|
||||
await $`brotli -k -f ${jsFile}`;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Build all targets
|
||||
let allOutputs: { path: string; kind: string }[] = [];
|
||||
for (const target of buildTargets) {
|
||||
const result = await buildRuntime(target);
|
||||
allOutputs = allOutputs.concat(result.outputs);
|
||||
}
|
||||
|
||||
const end = performance.now();
|
||||
|
||||
// Collect all compressed files
|
||||
const allCompressed = buildTargets.flatMap((target) => {
|
||||
const config = runtimeConfigs[target];
|
||||
if (!config) return [];
|
||||
return [
|
||||
{ path: path.join(outdir, `${config.outFilename}.gz`), kind: "compressed" },
|
||||
{ path: path.join(outdir, `${config.outFilename}.br`), kind: "compressed" },
|
||||
];
|
||||
});
|
||||
|
||||
const outputTable = allOutputs
|
||||
.concat(allCompressed)
|
||||
.filter((output) => existsSync(output.path))
|
||||
.map((output) => ({
|
||||
File: path.relative(process.cwd(), output.path),
|
||||
Size: formatFileSize(Bun.file(output.path).size),
|
||||
}));
|
||||
|
||||
console.table(outputTable);
|
||||
const buildTime = (end - start).toFixed(2);
|
||||
|
||||
// Copy all output files to runtime-scripts directory
|
||||
const runtimeScriptsDir = "runtime-scripts";
|
||||
if (!existsSync(runtimeScriptsDir)) {
|
||||
await $`mkdir -p ${runtimeScriptsDir}`;
|
||||
}
|
||||
|
||||
console.log(`\n📁 Copying files to ${runtimeScriptsDir}...`);
|
||||
for (const output of allOutputs.concat(allCompressed)) {
|
||||
if (existsSync(output.path)) {
|
||||
const filename = path.basename(output.path);
|
||||
await $`cp ${output.path} ${runtimeScriptsDir}/${filename}`;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\n✅ Runtime build completed in ${buildTime}ms\n`);
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
const env = process.env
|
||||
console.log('prebuild', env)
|
||||
Reference in New Issue
Block a user