delete the orphaned build script, and fix the second copy of the memo rule

scripts/build/runtime.ts had no caller after the build:editor scripts went. Its own usage
text gives away where it came from: --experiments, --tracking, --editor-setup, the same
phantom domain as the docs and examples cleaned up earlier. Nothing else references it —
the `./runtime` export in src/workspaces/types/package.json points at a different file,
which is untouched. helpers.ts stays; dashboard.ts and web.ts import it.

APP_CONVENTIONS.md carried its own copy of "React 19's compiler handles memoization. Never
use useCallback or useMemo." Correcting only CONVENTIONS.md would have left the two
contradicting each other, which is worse than either. Both now say the same thing and one
points at the other for the reasoning.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-31 18:12:19 +00:00
co-authored by Claude Opus 5
parent aac20bf112
commit a83f9cd378
2 changed files with 8 additions and 214 deletions
-212
View File
@@ -1,212 +0,0 @@
#!/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`);
+8 -2
View File
@@ -97,9 +97,15 @@ Within a file, order sections as:
5. Types (props types for sub-components) 5. Types (props types for sub-components)
6. Helper components / functions 6. Helper components / functions
## No useCallback or useMemo (React 19) ## Prefer plain values over memoization
React 19's compiler handles memoization. Never use `useCallback` or `useMemo`. Default to writing derivations plainly — most are cheap, and the memo costs more than the work. But
`useMemo`/`useCallback` are ordinary tools, not forbidden ones: reach for them when a value or callback
feeds a dependency array, when a computation is genuinely expensive, or when the prop goes to a memoized
child. See `CONVENTIONS.md` for the full rule and why the old "never, React 19 handles it" version was
wrong — the React Compiler is opt-in and is not installed here.
The examples below are still the right default shape:
```tsx ```tsx
// Plain function — not wrapped in useCallback // Plain function — not wrapped in useCallback