imrishabh18/corne-keyboard

The code defines and renders two surface-mount chip components with SMT pads, silkscreen outlines, and 3D CAD models (OBJ and STEP) for PCB assembly.

Version
2.0.21
License
unset
Stars
1

scripts/build-local-browser-runtime.mjs

import { createRequire } from 'node:module'
import { readFileSync, writeFileSync, mkdirSync, renameSync } from 'node:fs'
import { resolve, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
import { createHash } from 'node:crypto'

// When copied into the project's scripts/, no arguments are needed.
const here = dirname(fileURLToPath(import.meta.url))
const project = resolve(process.argv[2] ?? resolve(here, '..'))
const outputDir = resolve(process.argv[3] ?? resolve(project, '.tscircuit/local-runtime'))
const req = createRequire(resolve(project, 'package.json'))
const { build } = req('esbuild')
const packageJson = (name) => JSON.parse(readFileSync(resolve(project, 'node_modules', name, 'package.json'), 'utf8'))
const expectedVersions = {
  tscircuit: '0.0.2467',
  '@tscircuit/eval': '0.0.1365',
  '@tscircuit/core': '0.0.1861',
  '@tscircuit/checks': '0.0.185-corne.1',
  '@tscircuit/copper-pour-solver': '0.0.52',
  '@tscircuit/capacity-autorouter': '0.0.887',
}
for (const [name, expected] of Object.entries(expectedVersions)) {
  if (packageJson(name).version !== expected) throw new Error(`Review the local browser runtime before upgrading ${name} from ${expected}`)
}
const packageEntry = (name) => {
  try { return req.resolve(name) }
  catch {
    // core/eval expose only an ESM import condition, so createRequire.resolve
    // cannot select them. Their package metadata provides the exact ESM entry.
    const json = packageJson(name)
    return resolve(project, 'node_modules', name, json.exports?.['.']?.import ?? json.module ?? json.main)
  }
}
const aliases = Object.fromEntries([
  '@tscircuit/core', '@tscircuit/checks', '@tscircuit/copper-pour-solver',
  '@tscircuit/capacity-autorouter', '@tscircuit/eval', 'comlink', 'react', 'react/jsx-runtime',
].map((name) => [name, packageEntry(name)]))
aliases['@tscircuit/capacity-autorouter/package.json'] = resolve(project, 'node_modules/@tscircuit/capacity-autorouter/package.json')
const requiredMarkers = {
  '@tscircuit/core': ['corne-panel-pour-owning-board', 'corne-panel-owned-primitives'],
  '@tscircuit/checks': ['portsConnectedThroughPour', 'endpointTouchesConnectedPour'],
  '@tscircuit/copper-pour-solver': ['corne-polygon-pad-clearance', 'corne-pill-hole-rect-pad-obstacle'],
}
for (const [name, markers] of Object.entries(requiredMarkers)) {
  const source = readFileSync(aliases[name], 'utf8')
  for (const marker of markers) if (!source.includes(marker)) throw new Error(`Missing ${name} patch: ${marker}. Run the project's postinstall patches first.`)
}
mkdirSync(outputDir, { recursive: true })
const result = await build({
  absWorkingDir: project,
  entryPoints: [resolve(here, 'browser-worker-entrypoint.mjs')],
  outfile: resolve(outputDir, 'worker.js'),
  bundle: true, platform: 'browser', format: 'esm', minify: true, metafile: true,
  alias: aliases, nodePaths: [resolve(project, 'node_modules')],
  define: { 'process.env.NODE_ENV': '"production"' },
  loader: { '.wasm': 'dataurl' },
  // Manifold's guarded Node branch imports this; browsers use its embedded WASM.
  external: ['node:module'],
})
const inputPaths = Object.keys(result.metafile.inputs).map((p) => resolve(project, p))
for (const name of Object.keys(requiredMarkers)) {
  if (!inputPaths.includes(aliases[name])) throw new Error(`The local browser build did not include the patched ${name} entry`)
}
const browserPath = req.resolve('tscircuit/browser')
const browser = readFileSync(browserPath, 'utf8')
const worker = readFileSync(resolve(outputDir, 'worker.js'), 'utf8')
// tscircuit/browser contains runframe plus one embedded eval worker. Replace
// only that worker with the local build; the complete interactive UI remains.
const pattern = /URL\.createObjectURL\(new Blob\(\[atob\("([A-Za-z0-9+/=]+)"\)\], \{ type: 'application\/javascript' \}\)\)/g
if ([...browser.matchAll(pattern)].length !== 1) throw new Error('Expected exactly one embedded eval worker in tscircuit/browser')
const standalone = browser.replace(pattern, `URL.createObjectURL(new Blob([atob("${Buffer.from(worker).toString('base64')}")], { type: 'application/javascript' }))`)
const target = resolve(outputDir, 'runframe.js')
writeFileSync(`${target}.tmp`, standalone)
renameSync(`${target}.tmp`, target)
const sha256 = (data) => createHash('sha256').update(data).digest('hex')
writeFileSync(resolve(outputDir, 'manifest.json'), JSON.stringify({
  versions: expectedVersions,
  inputs: Object.fromEntries(Object.entries(aliases).map(([name, path]) => [name, { path, sha256: sha256(readFileSync(path)) }])),
  workerSha256: sha256(worker), runframeSha256: sha256(standalone),
  workerBytes: Buffer.byteLength(worker), runframeBytes: Buffer.byteLength(standalone),
}, null, 2) + '\n')
console.log(`Built live browser runtime: ${target}`)
console.log('Launch tsci dev with RUNFRAME_STANDALONE_FILE_PATH set to that absolute path.')