Make mono repo (#22)

This commit is contained in:
Sebastian
2025-08-15 13:03:59 +02:00
committed by GitHub
parent 268e1da983
commit 35566b2126
112 changed files with 122 additions and 88 deletions
-243
View File
@@ -1,243 +0,0 @@
import { spawnSync } from "node:child_process"
import { copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs"
import { dirname, join, resolve } from "path"
import { fileURLToPath } from "url"
import process from "process"
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const rootDir = resolve(__dirname, "..")
const licensePath = join(rootDir, "LICENSE")
const packageJson = JSON.parse(readFileSync(join(rootDir, "package.json"), "utf8"))
const args = process.argv.slice(2)
const buildLib = args.find((arg) => arg === "--lib")
const buildNative = args.find((arg) => arg === "--native")
const isDev = args.includes("--dev")
const variants = [
{ platform: "darwin", arch: "x64" },
{ platform: "darwin", arch: "arm64" },
{ platform: "linux", arch: "x64" },
{ platform: "linux", arch: "arm64" },
{ platform: "win32", arch: "x64" },
{ platform: "win32", arch: "arm64" },
]
if (!buildLib && !buildNative) {
console.error("Error: Please specify --lib, --native, or both")
process.exit(1)
}
const getZigTarget = (platform, arch) => {
const platformMap = { darwin: "macos", win32: "windows", linux: "linux" }
const archMap = { x64: "x86_64", arm64: "aarch64" }
return `${archMap[arch] ?? arch}-${platformMap[platform] ?? platform}`
}
const replaceLinks = (text) => {
return packageJson.homepage
? text.replace(
/(\[.*?\]\()(\.\/.*?\))/g,
(_, p1, p2) => `${p1}${packageJson.homepage}/blob/HEAD/${p2.replace("./", "")}`,
)
: text
}
const requiredFields = ["name", "version", "license", "repository", "description"]
const missingRequired = requiredFields.filter((field) => !packageJson[field])
if (missingRequired.length > 0) {
console.error(`Error: Missing required fields in package.json: ${missingRequired.join(", ")}`)
process.exit(1)
}
if (buildNative) {
console.log(`Building native ${isDev ? "dev" : "prod"} binaries...`)
const zigBuild = spawnSync("zig", ["build", `-Doptimize=${isDev ? "Debug" : "ReleaseFast"}`], {
cwd: join(rootDir, "src", "zig"),
stdio: "inherit",
})
if (zigBuild.error) {
console.error("Error: Zig is not installed or not in PATH")
process.exit(1)
}
if (zigBuild.status !== 0) {
console.error("Error: Zig build failed")
process.exit(1)
}
for (const { platform, arch } of variants) {
const nativeName = `${packageJson.name}-${platform}-${arch}`
const nativeDir = join(rootDir, "node_modules", nativeName)
const libDir = join(rootDir, "src", "zig", "lib", getZigTarget(platform, arch))
rmSync(nativeDir, { recursive: true, force: true })
mkdirSync(nativeDir, { recursive: true })
for (const name of ["libopentui", "opentui"]) {
for (const ext of [".so", ".dll", ".dylib"]) {
const src = join(libDir, `${name}${ext}`)
if (existsSync(src)) copyFileSync(src, join(nativeDir, `${name}${ext}`))
}
}
writeFileSync(
join(nativeDir, "package.json"),
JSON.stringify(
{
name: nativeName,
version: packageJson.version,
description: `Prebuilt ${platform}-${arch} binaries for ${packageJson.name}`,
license: packageJson.license,
author: packageJson.author,
homepage: packageJson.homepage,
repository: packageJson.repository,
bugs: packageJson.bugs,
keywords: [...(packageJson.keywords ?? []), "prebuild", "prebuilt"],
os: [platform],
cpu: [arch],
},
null,
2,
),
)
writeFileSync(
join(nativeDir, "README.md"),
replaceLinks(`## ${nativeName}\n\n> Prebuilt ${platform}-${arch} binaries for \`${packageJson.name}\`.`),
)
if (existsSync(licensePath)) copyFileSync(licensePath, join(nativeDir, "LICENSE"))
console.log("Built:", nativeName)
}
}
if (buildLib) {
console.log("Building library...")
const distDir = join(rootDir, "dist")
rmSync(distDir, { recursive: true, force: true })
mkdirSync(distDir, { recursive: true })
const externalDeps = [
...Object.keys(packageJson.optionalDependencies || {}),
...Object.keys(packageJson.peerDependencies || {}),
]
// Build main entry point
spawnSync(
"bun",
[
"build",
"--target=bun",
"--outdir=dist",
...externalDeps.flatMap((dep) => ["--external", dep]),
packageJson.module,
],
{
cwd: rootDir,
stdio: "inherit",
},
)
// Build additional entry points
const entryPoints = ["src/3d.ts"]
for (const entryPoint of entryPoints) {
spawnSync(
"bun",
["build", "--target=bun", "--outdir=dist", ...externalDeps.flatMap((dep) => ["--external", dep]), entryPoint],
{
cwd: rootDir,
stdio: "inherit",
},
)
}
console.log("Generating TypeScript declarations...")
const tsconfigBuildPath = join(rootDir, "tsconfig.build.json")
const tsconfigBuild = {
extends: "./tsconfig.json",
compilerOptions: {
declaration: true,
emitDeclarationOnly: true,
outDir: "./dist",
noEmit: false,
rootDir: "./src",
types: ["bun", "node", "three"],
skipLibCheck: true,
},
include: ["src/**/*"],
exclude: ["**/*.test.ts", "**/*.spec.ts", "src/examples/**/*", "src/benchmark/**/*", "src/zig/**/*"],
}
writeFileSync(tsconfigBuildPath, JSON.stringify(tsconfigBuild, null, 2))
const tscResult = spawnSync("npx", ["tsc", "-p", tsconfigBuildPath], {
cwd: rootDir,
stdio: "inherit",
})
rmSync(tsconfigBuildPath, { force: true })
if (tscResult.status !== 0) {
console.warn("Warning: TypeScript declaration generation failed")
} else {
console.log("TypeScript declarations generated")
}
// Configure exports for multiple entry points
const exports = {
".": {
import: "./index.js",
require: "./index.js",
types: "./index.d.ts",
},
"./3d": {
import: "./3d.js",
require: "./3d.js",
types: "./3d.d.ts",
},
}
const optionalDeps = Object.fromEntries(
variants.map(({ platform, arch }) => [`${packageJson.name}-${platform}-${arch}`, `^${packageJson.version}`]),
)
writeFileSync(
join(distDir, "package.json"),
JSON.stringify(
{
name: packageJson.name,
module: "index.js",
main: "index.js",
types: "index.d.ts",
type: packageJson.type,
version: packageJson.version,
description: packageJson.description,
keywords: packageJson.keywords,
license: packageJson.license,
author: packageJson.author,
homepage: packageJson.homepage,
repository: packageJson.repository,
bugs: packageJson.bugs,
exports,
dependencies: packageJson.dependencies,
optionalDependencies: {
...packageJson.optionalDependencies,
...optionalDeps,
},
},
null,
2,
),
)
writeFileSync(join(distDir, "README.md"), replaceLinks(readFileSync(join(rootDir, "README.md"), "utf8")))
if (existsSync(licensePath)) copyFileSync(licensePath, join(distDir, "LICENSE"))
console.log("Library built at:", distDir)
}
-179
View File
@@ -1,179 +0,0 @@
import { spawnSync } from "node:child_process"
import { existsSync, mkdirSync, readFileSync, rmSync, statSync } from "node:fs"
import { dirname, join, resolve } from "node:path"
import process from "node:process"
import { fileURLToPath } from "node:url"
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const rootDir = resolve(__dirname, "..")
const packedDir = join(rootDir, "packed")
const packageJson = JSON.parse(readFileSync(join(rootDir, "package.json"), "utf8"))
const args = process.argv.slice(2)
const skipBuild = args.includes("--skip-build")
const verbose = args.includes("--verbose")
if (!skipBuild) {
console.log("Building packages first...")
const buildResult = spawnSync("bun", ["run", "build"], {
cwd: rootDir,
stdio: "inherit",
})
if (buildResult.status !== 0) {
console.error("Error: Build failed")
process.exit(1)
}
}
const libDir = join(rootDir, "dist")
if (!existsSync(libDir)) {
console.error("Error: dist directory not found. Please run 'bun run build' first.")
process.exit(1)
}
rmSync(packedDir, { recursive: true, force: true })
mkdirSync(packedDir, { recursive: true })
const packagesToPack = []
const libPackageJson = JSON.parse(readFileSync(join(libDir, "package.json"), "utf8"))
packagesToPack.push({
name: libPackageJson.name,
dir: libDir,
type: "library",
})
for (const pkgName of Object.keys(libPackageJson.optionalDependencies || {}).filter((x) =>
x.startsWith(packageJson.name),
)) {
const nativeDir = join(rootDir, "node_modules", pkgName)
if (existsSync(nativeDir)) {
packagesToPack.push({
name: pkgName,
dir: nativeDir,
type: "native",
})
} else {
console.warn(`Warning: Native package directory not found: ${nativeDir}`)
}
}
console.log(`\nPacking ${packagesToPack.length} packages...\n`)
const packedFiles = []
const errors = []
for (const pkg of packagesToPack) {
try {
console.log(`Packing ${pkg.name} (${pkg.type})...`)
const packResult = spawnSync("npm", ["pack", "--pack-destination", packedDir], {
cwd: pkg.dir,
stdio: verbose ? "inherit" : "pipe",
})
if (packResult.status !== 0) {
const error = packResult.stderr?.toString() || "Unknown error"
errors.push({ package: pkg.name, error })
console.error(` Failed to pack ${pkg.name}`)
if (!verbose) {
console.error(` ${error.trim()}`)
}
continue
}
const output = packResult.stdout?.toString().trim()
const packedFile = output.split("\n").pop()
const fullPath = join(packedDir, packedFile)
if (existsSync(fullPath)) {
const stats = statSync(fullPath)
const sizeKB = (stats.size / 1024).toFixed(2)
packedFiles.push({
name: pkg.name,
type: pkg.type,
file: packedFile,
path: fullPath,
size: stats.size,
sizeKB,
})
console.log(` ✓ Packed: ${packedFile} (${sizeKB} KB)`)
if (verbose) {
const listResult = spawnSync("tar", ["-tzf", fullPath], {
cwd: packedDir,
})
if (listResult.status === 0) {
const files = listResult.stdout.toString().trim().split("\n")
console.log(` Files: ${files.length} files`)
if (files.length <= 20) {
files.forEach((f) => console.log(` - ${f}`))
} else {
files.slice(0, 10).forEach((f) => console.log(` - ${f}`))
console.log(` ... and ${files.length - 10} more files`)
}
}
}
} else {
errors.push({ package: pkg.name, error: "Packed file not found" })
console.error(` Packed file not found for ${pkg.name}`)
}
} catch (error) {
errors.push({ package: pkg.name, error: error.message })
console.error(` Error packing ${pkg.name}: ${error.message}`)
}
}
console.log("\n" + "=".repeat(60))
console.log("PACKING SUMMARY")
console.log("=".repeat(60))
if (packedFiles.length > 0) {
console.log(`\n✓ Successfully packed ${packedFiles.length} packages:`)
const library = packedFiles.filter((p) => p.type === "library")
const native = packedFiles.filter((p) => p.type === "native")
if (library.length > 0) {
console.log("\n Library:")
library.forEach((p) => {
console.log(` - ${p.file} (${p.sizeKB} KB)`)
})
}
if (native.length > 0) {
console.log("\n Native binaries:")
native.forEach((p) => {
console.log(` - ${p.file} (${p.sizeKB} KB)`)
})
}
const totalSize = packedFiles.reduce((sum, p) => sum + p.size, 0)
const totalSizeKB = (totalSize / 1024).toFixed(2)
console.log(`\n Total size: ${totalSizeKB} KB`)
}
if (errors.length > 0) {
console.log(`\n Failed to pack ${errors.length} packages:`)
errors.forEach((e) => {
console.log(` - ${e.package}: ${e.error}`)
})
}
if (packedFiles.length > 0) {
console.log(`\nPacked files saved to: ${packedDir}`)
console.log("\nYou can inspect the packed files with:")
console.log(" tar -tzf packed/<filename>.tgz # List contents")
console.log(" tar -xzf packed/<filename>.tgz # Extract contents")
console.log("\nTo publish these packages, run:")
console.log(" bun run publish")
}
if (errors.length > 0) {
process.exit(1)
}
-148
View File
@@ -1,148 +0,0 @@
import { spawnSync } from "node:child_process"
import { existsSync, readFileSync, writeFileSync } from "node:fs"
import { dirname, join, resolve } from "node:path"
import process from "node:process"
import { fileURLToPath } from "node:url"
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const rootDir = resolve(__dirname, "..")
const packageJson = JSON.parse(readFileSync(join(rootDir, "package.json"), "utf8"))
console.log(
`
Please confirm the following before continuing:
1. The "version" field in package.json has been updated.
2. The changes have been pushed to GitHub.
Continue? (y/n)
`.trim(),
)
const confirm = spawnSync(
"node",
[
"-e",
`
process.stdin.setRawMode(true);
process.stdin.resume();
process.stdin.on('data', (data) => {
const input = data.toString().toLowerCase();
if (input === 'y') process.exit(0);
if (input === 'n' || input === '\\x03') process.exit(1);
});
`,
],
{
shell: false,
stdio: "inherit",
},
)
if (confirm.status !== 0) {
console.log("Aborted.")
process.exit(1)
}
try {
const versions = JSON.parse(
spawnSync("npm", ["view", packageJson.name, "versions", "--json"], {}).stdout.toString().trim(),
)
if (versions.includes(packageJson.version)) {
console.error("Error: package.json version has not been incremented.")
console.warn("Please update the version before publishing.")
process.exit(1)
}
} catch {}
const libDir = join(rootDir, "dist")
if (!existsSync(libDir)) {
console.error("Error: dist directory not found. Please run 'bun run build' first.")
process.exit(1)
}
const mismatches = []
const packageJsons = {
[libDir]: JSON.parse(readFileSync(join(libDir, "package.json"), "utf8")),
}
for (const pkgName of Object.keys(packageJsons[libDir].optionalDependencies).filter((x) =>
x.startsWith(packageJson.name),
)) {
const nativeDir = join(rootDir, "node_modules", pkgName)
if (!existsSync(nativeDir)) {
console.error(`Error: Native package directory not found: ${nativeDir}`)
console.error("Please run 'bun run build:native' first.")
process.exit(1)
}
packageJsons[nativeDir] = JSON.parse(readFileSync(join(nativeDir, "package.json"), "utf8"))
}
for (const [dir, { name, version }] of Object.entries(packageJsons)) {
if (version !== packageJson.version) {
mismatches.push({
name,
dir,
expected: packageJson.version,
actual: version,
})
}
}
if (mismatches.length > 0) {
console.error("Error: Version mismatch detected between root package and build packages:")
mismatches.forEach((m) => console.error(` - ${m.name}: expected ${m.expected}, found ${m.actual}\n ^ "${m.dir}"`))
process.exit(1)
}
if (process.env.NPM_AUTH_TOKEN) {
const npmrcPath = join(process.env.HOME, ".npmrc")
const npmrcContent = `//registry.npmjs.org/:_authToken=${process.env.NPM_AUTH_TOKEN}\n`
if (existsSync(npmrcPath)) {
const existing = readFileSync(npmrcPath, "utf8")
if (!existing.includes("//registry.npmjs.org/:_authToken")) {
writeFileSync(npmrcPath, existing + "\n" + npmrcContent)
}
} else {
writeFileSync(npmrcPath, npmrcContent)
}
}
Object.entries(packageJsons).forEach(([dir, { name, version }]) => {
try {
const versions = JSON.parse(
spawnSync("npm", ["view", name, "versions", "--json"], {
cwd: dir,
})
.stdout.toString()
.trim(),
)
if (Array.isArray(versions) && versions.includes(version)) {
console.error("Error: package.json version has not been incremented.")
console.warn("Please update the version before publishing.")
process.exit(1)
}
} catch {}
const npmAuth = spawnSync("npm", ["whoami"], {})
if (npmAuth.status !== 0) {
console.error("Error: NPM authentication failed. Please run 'npm login' or ensure NPM_AUTH_TOKEN is set")
process.exit(1)
}
const publish = spawnSync("npm", ["publish", "--access=public"], {
cwd: dir,
stdio: "inherit",
})
if (publish.status !== 0) {
console.error(`Error: Failed to publish '${name}@${version}'.`)
process.exit(1)
}
console.log(`Package '${name}@${version}' published.`)
})