@hadimardanian/core
v1.1.1
Published
Use Gerber/drill files to create an SVG render of a finished PCB in Node.js or the browser.
Downloads
583
Maintainers
Readme
@hadimardanian/core
Turn a folder of Gerber / Excellon drill files into SVG previews of a finished PCB.
Works in Node.js (directory helpers) and the browser (pass File[] into read).
npm install @hadimardanian/coreQuick start — render a board folder
Point renderDirectory at a directory of fab files and an output folder. It writes:
top.svg/bottom.svg— finished board composites- one SVG per layer (e.g.
F_Cu.gbr.svg)
import {renderDirectory} from '@hadimardanian/core'
const {layers, outputFiles} = await renderDirectory('./my-board', './out')
console.log(
`Rendered ${layers.length} layers → ${outputFiles.length} SVG files`
)
// → out/top.svg, out/bottom.svg, out/<layer-filename>.svg …Board composites only (skip per-layer SVGs):
await renderDirectory('./my-board', './out', {boardOnly: true})Per-layer SVGs only (skip top.svg / bottom.svg):
await renderDirectory('./my-board', './out', {layersOnly: true})Batch-render many boards
When each board lives in its own subdirectory:
import fs from 'node:fs'
import path from 'node:path'
import {renderDirectory} from '@hadimardanian/core'
const boardsRoot = './boards' // each child folder is one PCB fab set
const outRoot = './out'
async function main() {
for (const boardName of fs.readdirSync(boardsRoot)) {
const boardPath = path.resolve(boardsRoot, boardName)
if (!fs.statSync(boardPath).isDirectory()) continue
const outDir = path.join(outRoot, boardName)
const {layers, outputFiles} = await renderDirectory(boardPath, outDir)
console.log(
`Rendering ${boardName}: ${layers.length} layers, ${outputFiles.length} files`
)
}
}
main().catch(error => {
console.error(error)
process.exit(1)
})Example layout:
boards/
clockblock/ ← Gerbers + drills
arduino-uno/
out/
clockblock/
top.svg
bottom.svg
…
arduino-uno/
…What goes in the input folder?
Pass the full fab set for one design: copper, soldermask, silkscreen, outline/profile, and drill.
- File selection is denylist-based (images, HTML, JSON, CAD reports, etc. are skipped). Odd Gerber extensions (e.g.
.gm,.gm13) are kept. - Layer type/side come from filenames (
@hadimardanian/identify-layers). - Unparseable files are soft-skipped by
read().
Helpers (also exported):
| Function | Role |
| -------- | ---- |
| collectLayerFiles(dir) | Recursively list fab candidates |
| shouldSkipLayerFile(path) | True for non-fab sidecars |
Step-by-step pipeline
If you need more control than renderDirectory:
import fs from 'node:fs/promises'
import {
read,
plot,
renderLayers,
renderBoard,
stringifySvg,
} from '@hadimardanian/core'
const files = [
'top-copper.gbr',
'top-solder-mask.gbr',
'top-silk-screen.gbr',
'bottom-copper.gbr',
'bottom-solder-mask.gbr',
'outline.gbr',
'drill.xnc',
]
const readResult = await read(files)
const plotResult = plot(readResult)
const layersResult = renderLayers(plotResult)
const board = renderBoard(layersResult)
await Promise.all([
fs.writeFile('top.svg', stringifySvg(board.top)),
fs.writeFile('bottom.svg', stringifySvg(board.bottom)),
])read → plot → renderLayers → renderBoard → stringifySvgIn the browser, pass File[] (e.g. from <input type="file" multiple>) to read instead of filesystem paths.
API snapshot
| Export | Notes |
| ------ | ----- |
| renderDirectory(src, dest, options?) | Node: folder → SVG files |
| collectLayerFiles / shouldSkipLayerFile | Node: fab file discovery |
| read, plot, renderLayers, renderBoard, stringifySvg | Main pipeline |
| BOARD_PALETTE, LAYER_COLORS | Fab / layer fill colors |
More detail: Usage Guide. CLI alternative: @hadimardanian/cli (tracer).
