feat: modernize netlistsvg to @paulgeorge/netlistsvg@1.0.0-alpha.1#1
feat: modernize netlistsvg to @paulgeorge/netlistsvg@1.0.0-alpha.1#1paulgeorge merged 54 commits intomasterfrom
Conversation
Cherry-pick of upstream PR nturley#151. When pre-computed ELK layout data was provided, drawModule result was discarded instead of being resolved through the promise chain.
Cherry-pick of upstream PR nturley#137.
Cherry-pick of upstream PR nturley#121. Converts recursive gather() to iterative loop to prevent stack overflow on large netlists.
Cherry-pick of upstream PR nturley#75. Adds a value label below the reference label on the generic analog component template.
Point repository, bugs, and homepage URLs to paulgeorge/netlistsvg. Add CONTRIBUTING.md with development setup and PR guidelines.
Documents PR nturley#92's approach, why it couldn't be merged directly, and outlines a plan for implementing the feature on the modernized codebase.
Replace all lodash utility calls with native JavaScript/TypeScript: _.map/_.forEach -> Object.entries().map/for...of _.filter -> Array.filter _.flatMap -> Array.flatMap _.find -> Array.find _.findIndex -> Array.findIndex _.includes -> Array.includes _.keys -> Object.keys _.max -> Math.max _.first/_.last -> array[0]/array[array.length-1] _.minBy -> Array.reduce _.isEqual -> direct property comparison _.mapValues -> Object.fromEntries/Object.entries
Update tsconfig.json: target ES2020, module commonjs, strict: true, esModuleInterop, skipLibCheck, resolveJsonModule, declaration. Fix all strict mode type errors across the codebase.
Remove tslint.json, add .eslintrc.yml with @typescript-eslint/parser and @typescript-eslint/eslint-plugin. Update lint script in package.json.
Add GitHub Actions CI workflow with Node 20/22 matrix testing. Add Dependabot config for weekly npm and Actions updates. Remove defunct Travis CI and AppVeyor configurations.
Add esbuild for browser bundle generation with a custom plugin to inline fs.readFileSync calls (replacing brfs transform). Update package.json scripts: add build, build:bundle, build:demo. Remove browserify, browserify-shim, and brfs config from package.json. Downgrade pre-existing lint errors to warnings in eslint config.
Add maintained fork notice, GitHub Actions CI badge, Node 20+ requirement, updated install instructions pointing to this fork, relative image paths, and updated status section.
Update jest.config.js with coverage collection settings targeting >80% line coverage. Add test:coverage script to package.json and simplify the test script to run jest directly.
Cover Port key/value operations and constant finding, Skin loading and template lookup, YosysModel interfaces and helpers, ELK graph construction, and main render/dumpLayout API.
CLI tests verify end-to-end rendering via bin/netlistsvg.js. Example tests replace test-all.js with Jest-based regression tests for all 6 digital and 5 analog example netlists.
Edge case tests cover malformed JSON, empty netlists, invalid skins, and FlatModule utility functions. Snapshot tests capture SVG output for mux4, up3down5, hyperedges, and resistor_divider to prevent visual regressions.
Fixes upstream issue nturley#136 - package-lock.json should be committed for reproducible builds. Also ignores coverage/ output directory.
Drop-in upgrade with no API changes. Includes bug fixes for port ordering, node sizing, and component packing that may improve layout quality for netlists.
Scoped package name avoids conflicts with the original npm package. Version reset to 1.0.0-alpha.1 with clean semver going forward. Original author credited as contributor.
|
Claude finished @paulgeorge's task in 2m 19s —— View job Code ReviewThis is a solid modernization effort — the dependency cleanup, TypeScript upgrade, CI replacement, and test expansion are all well-executed. Here are findings across the codebase. Bugs / Correctness Issues1. Silent error swallowing in promise = elk.layout(kgraph, { layoutOptions: layoutProps.layoutEngine })
.then((g: any) => drawModule(g, flatModule))
.catch((e: any) => { console.error(e); }); // ← resolves undefined, never rejectsWhen ELK fails, 2. Module-level mutable state in export let wireNameLookup: WireNameLookup = {};
export let dummyNum: number = 0;
export let edgeIndex: number = 0;These are reset inside 3. const gap = Number(inPorts[1][1]['s:y']) - Number(inPorts[0][1]['s:y']);
4. Missing error check in CLI — fs.readFile(elkJsonPath, (err, elkString) => {
callback(skinData, netlistData, json5.parse(elkString)); // ← err ignored
});The Code Quality Issues5. Dead code —
6. for (const index in g.edges) {
7. Prototype pollution risk in const map: StringToBool = {};
inStrs.forEach((str) => { map[str] = true; });
return Object.keys(map);A plain object as a set is vulnerable to net names like 8. Non-null assertion on potentially-unset value — if (!vals!.layoutEngine) {
vals!.layoutEngine = {};
}
return vals!;
Minor Issues9. Duplicate script in 10. CI doesn't run lint — 11. Weak test assertion — What's Good
|
The .catch handler was logging the error and resolving with undefined, causing callers to silently receive no SVG output on layout failure.
Removes non-null assertions that would crash on skins without s:properties element.
Classifies 11 bot review findings as FIX (4), IMPROVE (4), or NOISE (3). All findings were pre-existing issues, not introduced by our changes.
Includes test plan checklist and a note that reviewers should focus on changed/new code, not pre-existing issues in unchanged files.
|
@claude just review changes, do not update code |
|
Claude finished @paulgeorge's task in 3m 9s —— View job Code Review
Good progress since the last review pass — six of the eleven original findings are fixed. Below is a focused assessment of what remains and a few new findings. Still-Open Bugs from Previous Review1. Callback form never calls if (typeof done === 'function') {
promise.then((output) => {
done(null, output as string);
}).catch((reason) => {
throw Error(reason); // ← unhandled rejection, done() never called
});
}
2.
3. if (this.inputPorts.length > this.outputPorts.length) {
const gap = Number(inPorts[1][1]['s:y']) - Number(inPorts[0][1]['s:y']); // ← inPorts[1] uncheckedThe condition tests 4. Prototype pollution in The plain-object-as-set pattern is still present and the New Findings5. if (signalsByConstantName.hasOwnProperty(constName)) {
6. const sourceParentKey: string = sourcePort.parentNode.key; // lowercase
const targetParentKey: string = targetPort.parentNode.key; // lowercase
7. const netId = ElkModel.wireNameLookup[e.id];
const numWires = netId.split(',').length - 2; // throws if netId is undefinedIf What's Fixed Since Last Review
SummaryFour bugs from the original review remain open (1–4 above), plus three new findings (5–7). The most impactful are: the callback error path (callers on a server will silently lose errors), the |
Test failures now appear as inline annotations on PR file diffs. Also runs coverage in CI. Reporter only activates in GitHub Actions.
Fixes Node.js 20 deprecation warning. Node 20 actions will be forced to Node 24 starting June 2, 2026.

Summary
Complete modernization of netlistsvg, bringing the unmaintained upstream (last commit Jan 2024) up to current standards.
Upstream PRs merged
--layoutoption returning undefinedDependencies
package-lock.json(fixes upstream could you please commit and maintain a package lock file? nturley/netlistsvg#136)CI & Build
Test suite
Documentation
Test plan
npm ciinstalls cleanly (390 packages, 0 vulnerabilities)npx tsccompiles without errorsnpm run build:bundleproduces valid esbuild bundlenpm test— 83/83 tests pass across 12 suitesnpm audit— 0 vulnerabilities--layoutflag works end-to-end