muse/book-reading-clip-lamp

A Wi‑Fi/USB‑C ESP32‑S3 reading lamp with OV5640 camera, microphone, speaker amplifier, rechargeable Li‑Po power management, six-LED boost light, USB protection/charging detection, and a clip/gooseneck enclosure with external FlexPIFA antenna.

Version
0.4.3
License
unset
Stars
0

check-schematic-style.mjs

import { createHash } from "node:crypto"
import { mkdir, readFile, writeFile } from "node:fs/promises"
import { resolve } from "node:path"
import { pathToFileURL } from "node:url"
import { spawnSync } from "node:child_process"
import { checkSheetBounds } from "./schematic-sheet-bounds.mjs"

// Use the official self-contained bundle: installing the analyzer's source
// package can hoist older dependencies over the pinned PCB toolchain.
const packageName = "@tscircuit/circuit-json-schematic-placement-analysis"
const baseUrl = `https://jscdn.tscircuit.com/${packageName}`
const sha256 = value => createHash("sha256").update(value).digest("hex")
async function download(url) {
  const response = await fetch(url, { signal: AbortSignal.timeout(60_000) })
  if (!response.ok) throw new Error(`HTTP ${response.status}: ${url}`)
  return await response.text()
}

// Always resolve latest online. Fail on lookup failure instead of silently
// reporting an older cached analyzer as latest.
const metadata = JSON.parse(await download(`${baseUrl}/latest/package.json`))
if (metadata.name !== packageName || !/^\d+\.\d+\.\d+$/.test(metadata.version)) {
  throw new Error("Unexpected schematic analyzer metadata")
}
const version = metadata.version
const bundleUrl = `${baseUrl}/${version}/dist/browser.js`
const bundle = await download(bundleUrl)
const cache = resolve(".tscircuit/schematic-analysis")
await mkdir(cache, { recursive: true })
const bundlePath = resolve(cache, `${version}.mjs`)
await writeFile(bundlePath, bundle)
console.log(`Schematic style analyzer: ${packageName}@${version} (latest)`)

// Default to a fresh full build; --built explicitly analyzes the current build
// for workflows that already built it. Never overwrite dist with a partial PCB.
if (!process.argv.includes("--built")) {
  const build = spawnSync("bun", ["run", "build"], { stdio: "inherit" })
  if (build.error) throw build.error
  if (build.status !== 0) process.exit(build.status ?? 1)
}
const inputPath = resolve("dist/index/circuit.json")
const input = await readFile(inputPath, "utf8")
const circuit = JSON.parse(input)
if (!Array.isArray(circuit) || !circuit.some(item => item.type === "schematic_component")) {
  throw new Error("Built circuit has no schematic components")
}
const { analyzeSchematicPlacement } = await import(pathToFileURL(bundlePath).href)
// No issueTypes filter: every available analyzer rule runs across every sheet.
const analysis = analyzeSchematicPlacement(circuit)
const styleIssues = analysis.getIssues()
// Upstream style rules do not test page containment, and core's page check
// omits schematic_text. Keep this additional check explicit in the report.
const boundaryIssues = checkSheetBounds(circuit)
const issues = [...styleIssues, ...boundaryIssues]
const report = {
  analyzer: packageName, version, latestCheckedAt: new Date().toISOString(),
  bundleUrl, bundleSha256: sha256(bundle), input: "dist/index/circuit.json",
  inputSha256: sha256(input), allRulesEnabled: true,
  sheets: circuit.filter(item => item.type === "schematic_sheet").length,
  issueCount: issues.length, styleIssueCount: styleIssues.length,
  boundaryIssueCount: boundaryIssues.length, boundaryChecker: "schematic-sheet-bounds.mjs",
  counts: analysis.getIssueCounts(), issues,
}
await mkdir("checks/schematic-style", { recursive: true })
await writeFile("checks/schematic-style/report.json", JSON.stringify(report, null, 2) + "\n")
const boundaryText = boundaryIssues.map(issue => JSON.stringify(issue)).join("\n")
await writeFile("checks/schematic-style/issues.txt", [analysis.toString(), boundaryText].filter(Boolean).join("\n") + "\n")
if (issues.length) {
  if (styleIssues.length) console.error(analysis.toString())
  if (boundaryIssues.length) console.error(boundaryText)
  console.error(`FAIL: ${styleIssues.length} schematic style issue(s), ${boundaryIssues.length} sheet-boundary issue(s)`)
  process.exitCode = 1
} else {
  console.log(`PASS: all ${Object.keys(report.counts).length} style rule types plus text-aware sheet boundaries, ${report.sheets} sheets, 0 issues`)
}