Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ DIST_DIR = dist

SRC_FILES = index.ts lib/version.ts $(shell find lib -type f -name '*.ts')
TEST_FILES = $(shell find test -type f -name '*.ts')
BUILD_FILES = $(addprefix $(DIST_DIR)/, $(MOD).cjs.js $(MOD).esm.js $(MOD).min.js $(MOD).js)
BUILD_FILES = $(addprefix $(DIST_DIR)/, $(MOD).cjs $(MOD).esm.js $(MOD).min.js $(MOD).js)

.PHONY: all bench clean test dist lint build release node_modules

Expand Down
2 changes: 1 addition & 1 deletion build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ async function build(): Promise<void> {
// 1. CommonJS (CJS) - For Node.js `require()`
await esbuild({
...sharedConfig,
outfile: 'dist/graphlib.cjs.js',
outfile: 'dist/graphlib.cjs',
format: 'cjs',
platform: 'node',
});
Expand Down
2 changes: 1 addition & 1 deletion dist/graphlib.cjs.js → dist/graphlib.cjs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
"use strict";var F=Object.defineProperty;var H=Object.getOwnPropertyDescriptor;var U=Object.getOwnPropertyNames;var Y=Object.prototype.hasOwnProperty;var x=(s,e)=>{for(var t in e)F(s,t,{get:e[t],enumerable:!0})},z=(s,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of U(e))!Y.call(s,i)&&i!==t&&F(s,i,{get:()=>e[i],enumerable:!(r=H(e,i))||r.enumerable});return s};var K=s=>z(F({},"__esModule",{value:!0}),s);var oe={};x(oe,{Graph:()=>l,alg:()=>v,json:()=>y,version:()=>I});module.exports=K(oe);var l=class{constructor(e){this._isDirected=!0;this._isMultigraph=!1;this._isCompound=!1;this._nodes={};this._in={};this._preds={};this._out={};this._sucs={};this._edgeObjs={};this._edgeLabels={};this._nodeCount=0;this._edgeCount=0;this._defaultNodeLabelFn=()=>{};this._defaultEdgeLabelFn=()=>{};e&&(this._isDirected="directed"in e?e.directed:!0,this._isMultigraph="multigraph"in e?e.multigraph:!1,this._isCompound="compound"in e?e.compound:!1),this._isCompound&&(this._parent={},this._children={},this._children["\0"]={})}isDirected(){return this._isDirected}isMultigraph(){return this._isMultigraph}isCompound(){return this._isCompound}setGraph(e){return this._label=e,this}graph(){return this._label}setDefaultNodeLabel(e){return typeof e!="function"?this._defaultNodeLabelFn=()=>e:this._defaultNodeLabelFn=e,this}nodeCount(){return this._nodeCount}nodes(){return Object.keys(this._nodes)}sources(){return this.nodes().filter(e=>Object.keys(this._in[e]).length===0)}sinks(){return this.nodes().filter(e=>Object.keys(this._out[e]).length===0)}setNodes(e,t){return e.forEach(r=>{t!==void 0?this.setNode(r,t):this.setNode(r)}),this}setNode(e,t){return e in this._nodes?(arguments.length>1&&(this._nodes[e]=t),this):(this._nodes[e]=arguments.length>1?t:this._defaultNodeLabelFn(e),this._isCompound&&(this._parent[e]="\0",this._children[e]={},this._children["\0"][e]=!0),this._in[e]={},this._preds[e]={},this._out[e]={},this._sucs[e]={},++this._nodeCount,this)}node(e){return this._nodes[e]}hasNode(e){return e in this._nodes}removeNode(e){if(e in this._nodes){let t=r=>this.removeEdge(this._edgeObjs[r]);delete this._nodes[e],this._isCompound&&(this._removeFromParentsChildList(e),delete this._parent[e],this.children(e).forEach(r=>{this.setParent(r)}),delete this._children[e]),Object.keys(this._in[e]).forEach(t),delete this._in[e],delete this._preds[e],Object.keys(this._out[e]).forEach(t),delete this._out[e],delete this._sucs[e],--this._nodeCount}return this}setParent(e,t){if(!this._isCompound)throw new Error("Cannot set parent in a non-compound graph");if(t===void 0)t="\0";else{t+="";for(let r=t;r!==void 0;r=this.parent(r))if(r===e)throw new Error("Setting "+t+" as parent of "+e+" would create a cycle");this.setNode(t)}return this.setNode(e),this._removeFromParentsChildList(e),this._parent[e]=t,this._children[t][e]=!0,this}parent(e){if(this._isCompound){let t=this._parent[e];if(t!=="\0")return t}}children(e="\0"){if(this._isCompound){let t=this._children[e];if(t)return Object.keys(t)}else{if(e==="\0")return this.nodes();if(this.hasNode(e))return[]}return[]}predecessors(e){let t=this._preds[e];if(t)return Object.keys(t)}successors(e){let t=this._sucs[e];if(t)return Object.keys(t)}neighbors(e){let t=this.predecessors(e);if(t){let r=new Set(t),i=this.successors(e);if(i)for(let n of i)r.add(n);return Array.from(r.values())}}isLeaf(e){var r;let t;return this.isDirected()?t=this.successors(e):t=this.neighbors(e),((r=t==null?void 0:t.length)!=null?r:0)===0}filterNodes(e){let t=new this.constructor({directed:this._isDirected,multigraph:this._isMultigraph,compound:this._isCompound});t.setGraph(this.graph()),Object.entries(this._nodes).forEach(([n,o])=>{e(n)&&t.setNode(n,o)}),Object.values(this._edgeObjs).forEach(n=>{t.hasNode(n.v)&&t.hasNode(n.w)&&t.setEdge(n,this.edge(n))});let r={},i=n=>{let o=this.parent(n);return!o||t.hasNode(o)?(r[n]=o,o):o in r?r[o]:i(o)};return this._isCompound&&t.nodes().forEach(n=>t.setParent(n,i(n))),t}setDefaultEdgeLabel(e){return typeof e!="function"?this._defaultEdgeLabelFn=()=>e:this._defaultEdgeLabelFn=e,this}edgeCount(){return this._edgeCount}edges(){return Object.values(this._edgeObjs)}setPath(e,t){return e.reduce((r,i)=>(t!==void 0?this.setEdge(r,i,t):this.setEdge(r,i),i)),this}setEdge(e,t,r,i){let n,o,d,h,c=!1;typeof e=="object"&&e!==null&&"v"in e?(n=e.v,o=e.w,d=e.name,arguments.length===2&&(h=t,c=!0)):(n=e,o=t,d=i,arguments.length>2&&(h=r,c=!0)),n=""+n,o=""+o,d!==void 0&&(d=""+d);let a=_(this._isDirected,n,o,d);if(a in this._edgeLabels)return c&&(this._edgeLabels[a]=h),this;if(d!==void 0&&!this._isMultigraph)throw new Error("Cannot set a named edge when isMultigraph = false");this.setNode(n),this.setNode(o),this._edgeLabels[a]=c?h:this._defaultEdgeLabelFn(n,o,d);let u=Q(this._isDirected,n,o,d);return n=u.v,o=u.w,Object.freeze(u),this._edgeObjs[a]=u,k(this._preds[o],n),k(this._sucs[n],o),this._in[o][a]=u,this._out[n][a]=u,this._edgeCount++,this}edge(e,t,r){let i=arguments.length===1?R(this._isDirected,e):_(this._isDirected,e,t,r);return this._edgeLabels[i]}edgeAsObj(e,t,r){let i=arguments.length===1?this.edge(e):this.edge(e,t,r);return typeof i!="object"||i===null?{label:i}:i}hasEdge(e,t,r){return(arguments.length===1?R(this._isDirected,e):_(this._isDirected,e,t,r))in this._edgeLabels}removeEdge(e,t,r){let i=arguments.length===1?R(this._isDirected,e):_(this._isDirected,e,t,r),n=this._edgeObjs[i];if(n){let o=n.v,d=n.w;delete this._edgeLabels[i],delete this._edgeObjs[i],P(this._preds[d],o),P(this._sucs[o],d),delete this._in[d][i],delete this._out[o][i],this._edgeCount--}return this}inEdges(e,t){return this.isDirected()?this.filterEdges(this._in[e],e,t):this.nodeEdges(e,t)}outEdges(e,t){return this.isDirected()?this.filterEdges(this._out[e],e,t):this.nodeEdges(e,t)}nodeEdges(e,t){if(e in this._nodes)return this.filterEdges({...this._in[e],...this._out[e]},e,t)}_removeFromParentsChildList(e){delete this._children[this._parent[e]][e]}filterEdges(e,t,r){if(!e)return;let i=Object.values(e);return r?i.filter(n=>n.v===t&&n.w===r||n.v===r&&n.w===t):i}};function k(s,e){s[e]?s[e]++:s[e]=1}function P(s,e){s[e]!==void 0&&!--s[e]&&delete s[e]}function _(s,e,t,r){let i=""+e,n=""+t;if(!s&&i>n){let o=i;i=n,n=o}return i+""+n+""+(r===void 0?"\0":r)}function Q(s,e,t,r){let i=""+e,n=""+t;if(!s&&i>n){let d=i;i=n,n=d}let o={v:i,w:n};return r&&(o.name=r),o}function R(s,e){return _(s,e.v,e.w,e.name)}var I="4.0.4-pre";var y={};x(y,{read:()=>X,write:()=>$});function $(s){let e={options:{directed:s.isDirected(),multigraph:s.isMultigraph(),compound:s.isCompound()},nodes:q(s),edges:B(s)},t=s.graph();return t!==void 0&&(e.value=structuredClone(t)),e}function q(s){return s.nodes().map(e=>{let t=s.node(e),r=s.parent(e),i={v:e};return t!==void 0&&(i.value=t),r!==void 0&&(i.parent=r),i})}function B(s){return s.edges().map(e=>{let t=s.edge(e),r={v:e.v,w:e.w};return e.name!==void 0&&(r.name=e.name),t!==void 0&&(r.value=t),r})}function X(s){let e=new l(s.options);return s.value!==void 0&&e.setGraph(s.value),s.nodes.forEach(t=>{e.setNode(t.v,t.value),t.parent&&e.setParent(t.v,t.parent)}),s.edges.forEach(t=>{e.setEdge({v:t.v,w:t.w,name:t.name},t.value)}),e}var v={};x(v,{CycleException:()=>p,bellmanFord:()=>L,components:()=>D,dijkstra:()=>E,dijkstraAll:()=>O,findCycles:()=>j,floydWarshall:()=>C,isAcyclic:()=>T,postorder:()=>S,preorder:()=>M,prim:()=>V,shortestPaths:()=>J,tarjan:()=>w,topsort:()=>N});var Z=()=>1;function L(s,e,t,r){return ee(s,String(e),t||Z,r||function(i){var n;return(n=s.outEdges(i))!=null?n:[]})}function ee(s,e,t,r){let i={},n,o=0,d=s.nodes(),h=function(u){let g=i[u.v],f=i[u.w];if(!g||!f)return;let m=t(u);g.distance+m<f.distance&&(i[u.w]={distance:g.distance+m,predecessor:u.v},n=!0)},c=function(){d.forEach(function(u){r(u).forEach(function(g){let f=g.v===u?g.v:g.w,m=f===g.v?g.w:g.v;h({v:f,w:m})})})};d.forEach(function(u){let g=u===e?0:Number.POSITIVE_INFINITY;i[u]={distance:g,predecessor:""}});let a=d.length;for(let u=1;u<a&&(n=!1,o++,c(),!!n);u++);if(o===a-1&&(n=!1,c(),n))throw new Error("The graph contains a negative weight cycle");return i}function D(s){let e={},t=[],r;function i(n){var o,d;n in e||(e[n]=!0,r.push(n),(o=s.successors(n))==null||o.forEach(i),(d=s.predecessors(n))==null||d.forEach(i))}return s.nodes().forEach(function(n){r=[],i(n),r.length&&t.push(r)}),t}var b=class{constructor(){this._arr=[];this._keyIndices={}}size(){return this._arr.length}keys(){return this._arr.map(e=>e.key)}has(e){return e in this._keyIndices}priority(e){let t=this._keyIndices[e];if(t!==void 0)return this._arr[t].priority}min(){if(this.size()===0)throw new Error("Queue underflow");return this._arr[0].key}add(e,t){let r=this._keyIndices,i=String(e);if(!(i in r)){let n=this._arr,o=n.length;return r[i]=o,n.push({key:i,priority:t}),this._decrease(o),!0}return!1}removeMin(){if(this.size()===0)throw new Error("Queue underflow");this._swap(0,this._arr.length-1);let e=this._arr.pop();return delete this._keyIndices[e.key],this._heapify(0),e.key}decrease(e,t){let r=this._keyIndices[e];if(r===void 0)throw new Error(`Key not found: ${e}`);let i=this._arr[r].priority;if(t>i)throw new Error(`New priority is greater than current priority. Key: ${e} Old: ${i} New: ${t}`);this._arr[r].priority=t,this._decrease(r)}_heapify(e){let t=this._arr,r=2*e,i=r+1,n=e;r<t.length&&(n=t[r].priority<t[n].priority?r:n,i<t.length&&(n=t[i].priority<t[n].priority?i:n),n!==e&&(this._swap(e,n),this._heapify(n)))}_decrease(e){let t=this._arr,r=t[e].priority,i;for(;e!==0&&(i=e>>1,!(t[i].priority<r));)this._swap(e,i),e=i}_swap(e,t){let r=this._arr,i=this._keyIndices,n=r[e],o=r[t];r[e]=o,r[t]=n,i[o.key]=e,i[n.key]=t}};var te=()=>1;function E(s,e,t,r){let i=function(n){var o;return(o=s.outEdges(n))!=null?o:[]};return re(s,String(e),t||te,r||i)}function re(s,e,t,r){let i={},n=new b,o,d,h=function(c){let a=c.v!==o?c.v:c.w,u=i[a];if(!u)return;let g=t(c),f=d.distance+g;if(g<0)throw new Error("dijkstra does not allow negative edge weights. Bad edge: "+c+" Weight: "+g);f<u.distance&&(u.distance=f,u.predecessor=o,n.decrease(a,f))};for(s.nodes().forEach(function(c){let a=c===e?0:Number.POSITIVE_INFINITY;i[c]={distance:a,predecessor:""},n.add(c,a)});n.size()>0;){o=n.removeMin();let c=i[o];if(!c||c.distance===Number.POSITIVE_INFINITY)break;d=c,r(o).forEach(h)}return i}function O(s,e,t){return s.nodes().reduce(function(r,i){return r[i]=E(s,i,e,t),r},{})}function w(s){let e=0,t=[],r={},i=[];function n(o){var h;let d=r[o]={onStack:!0,lowlink:e,index:e++};if(t.push(o),(h=s.successors(o))==null||h.forEach(function(c){if(c in r){let a=r[c];a!=null&&a.onStack&&(d.lowlink=Math.min(d.lowlink,a.index))}else{n(c);let a=r[c];a&&(d.lowlink=Math.min(d.lowlink,a.lowlink))}}),d.lowlink===d.index){let c=[],a;do{a=t.pop();let u=r[a];u&&(u.onStack=!1),c.push(a)}while(o!==a);i.push(c)}}return s.nodes().forEach(function(o){o in r||n(o)}),i}function j(s){return w(s).filter(function(e){var r;let t=e[0];return t?e.length>1||e.length===1&&((r=s.outEdges(t,t))!=null?r:[]).length>0:!1})}var ne=()=>1;function C(s,e,t){return ie(s,e||ne,t||function(r){var i;return(i=s.outEdges(r))!=null?i:[]})}function ie(s,e,t){let r={},i=s.nodes();return i.forEach(function(n){let o={};r[n]=o,o[n]={distance:0,predecessor:""},i.forEach(function(d){n!==d&&(o[d]={distance:Number.POSITIVE_INFINITY,predecessor:""})}),t(n).forEach(function(d){let h=d.v===n?d.w:d.v,c=e(d);o[h]={distance:c,predecessor:n}})}),i.forEach(function(n){let o=r[n];o&&i.forEach(function(d){let h=r[d];h&&i.forEach(function(c){let a=h[n],u=o[c],g=h[c];if(a&&u&&g){let f=a.distance+u.distance;f<g.distance&&(g.distance=f,g.predecessor=u.predecessor)}})})}),r}var p=class extends Error{constructor(e){super(e),this.name="CycleException"}};function N(s){let e={},t={},r=[];function i(n){var o;if(n in t)throw new p;n in e||(t[n]=!0,e[n]=!0,(o=s.predecessors(n))==null||o.forEach(i),delete t[n],r.push(n))}if(s.sinks().forEach(i),Object.keys(e).length!==s.nodeCount())throw new p;return r}function T(s){try{N(s)}catch(e){if(e instanceof p)return!1;throw e}return!0}function A(s,e,t,r,i){Array.isArray(e)||(e=[e]);let n=(d=>{var h;return(h=s.isDirected()?s.successors(d):s.neighbors(d))!=null?h:[]}),o={};return e.forEach(function(d){if(!s.hasNode(d))throw new Error("Graph does not have node: "+d);i=W(s,d,t==="post",o,n,r,i)}),i}function W(s,e,t,r,i,n,o){return e in r||(r[e]=!0,t||(o=n(o,e)),i(e).forEach(function(d){o=W(s,d,t,r,i,n,o)}),t&&(o=n(o,e))),o}function G(s,e,t){return A(s,e,t,function(r,i){return r.push(i),r},[])}function S(s,e){return G(s,e,"post")}function M(s,e){return G(s,e,"pre")}function V(s,e){var c;let t=new l,r={},i=new b,n;function o(a){let u=a.v===n?a.w:a.v,g=i.priority(u);if(g!==void 0){let f=e(a);f<g&&(r[u]=n,i.decrease(u,f))}}if(s.nodeCount()===0)return t;s.nodes().forEach(function(a){i.add(a,Number.POSITIVE_INFINITY),t.setNode(a)});let d=s.nodes()[0];d!==void 0&&i.decrease(d,0);let h=!1;for(;i.size()>0;){if(n=i.removeMin(),n in r)t.setEdge(n,r[n]);else{if(h)throw new Error("Input graph is not connected: "+s);h=!0}(c=s.nodeEdges(n))==null||c.forEach(o)}return t}function J(s,e,t,r){return se(s,e,t,r!=null?r:(i=>{var n;return(n=s.outEdges(i))!=null?n:[]}))}function se(s,e,t,r){if(t===void 0)return E(s,e,t,r);let i=!1,n=s.nodes();for(let o=0;o<n.length;o++){let d=n[o];if(d===void 0)continue;let h=r(d);for(let c=0;c<h.length;c++){let a=h[c];if(!a)continue;let u=a.v===d?a.v:a.w,g=u===a.v?a.w:a.v;t({v:u,w:g})<0&&(i=!0)}if(i)return L(s,e,t,r)}return E(s,e,t,r)}0&&(module.exports={Graph,alg,json,version});
//# sourceMappingURL=graphlib.cjs.js.map
//# sourceMappingURL=graphlib.cjs.map
File renamed without changes.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"David Newell"
],
"license": "MIT",
"main": "dist/graphlib.cjs.js",
"type": "module",
"main": "dist/graphlib.cjs",
"module": "dist/graphlib.esm.js",
"scripts": {
"build": "npm run build:types && tsx build.ts",
Expand All @@ -28,7 +29,7 @@
".": {
"types": "./dist/types/index.d.ts",
"import": "./dist/graphlib.esm.js",
"require": "./dist/graphlib.cjs.js"
"require": "./dist/graphlib.cjs"
},
"./package.json": "./package.json"
},
Expand Down
92 changes: 92 additions & 0 deletions test/dist-exports.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Regression test for the published dist package format (dagrejs/graphlib #233).
//
// test/bundle-test.ts imports ../index (source) via ts-jest, so it cannot catch a
// dist-format bug. This test exercises the BUILT dist bundles through real
// runtimes (tsx for ESM, node for CJS) by importing them at an absolute path.
//
// Importing by absolute path is what makes this faithful: Node/tsx reads the
// "type" field from the dist file's nearest package.json to decide whether a
// ".js" file is ESM or CJS. Without "type": "module" the ESM bundle
// (dist/graphlib.esm.js, ESM syntax) is treated as CJS, so named imports are
// undefined and `new Graph()` throws. Adding "type": "module" fixes the ESM
// path; renaming the CJS bundle to .cjs keeps `require()` working under it.

import {execFileSync} from 'child_process';
import {existsSync, mkdtempSync, rmSync, writeFileSync} from 'fs';
import {tmpdir} from 'os';
import {join, resolve} from 'path';

const repoRoot = process.cwd();
const esmPath = resolve(repoRoot, 'dist', 'graphlib.esm.js');
const cjsPath = resolve(repoRoot, 'dist', 'graphlib.cjs');

// ESM smoke check run under tsx — mirrors the #233 repro. Under a correct
// package.json the named exports resolve; under the buggy format only `default`
// is present, so `Graph` is undefined and `new Graph()` throws.
const esmCheck = `import {Graph, alg, json, version} from ${JSON.stringify(esmPath)};

const problems = [];
if (typeof Graph !== 'function') problems.push('Graph is ' + typeof Graph + ', expected function');
if (typeof alg !== 'object') problems.push('alg is ' + typeof alg + ', expected object');
if (typeof json !== 'object') problems.push('json is ' + typeof json + ', expected object');
if (typeof version !== 'string') problems.push('version is ' + typeof version + ', expected string');
try {
const g = new Graph();
g.setNode('a');
if (!g.hasNode('a')) problems.push('Graph operation failed');
} catch (e) {
problems.push('new Graph() threw: ' + e.message);
}
if (problems.length) { console.error('ESM FAIL: ' + problems.join('; ')); process.exit(1); }
console.log('ESM OK');
`;

// CJS smoke check run under node — guards the CJS regression that a naive
// "type": "module" (without renaming the CJS bundle to .cjs) would introduce:
// `require()` of a .js file under "type": "module" throws ERR_REQUIRE_ESM.
const cjsCheck = `const {Graph} = require(${JSON.stringify(cjsPath)});
const g = new Graph();
g.setNode('a');
if (!g.hasNode('a')) { console.error('CJS FAIL: Graph operation failed'); process.exit(1); }
console.log('CJS OK');
`;

describe('dist exports', () => {
let tmpDir: string;

beforeAll(() => {
if (!existsSync(esmPath) || !existsSync(cjsPath)) {
execFileSync('npm', ['run', 'build'], {
cwd: repoRoot,
stdio: 'pipe',
shell: process.platform === 'win32',
});
}
tmpDir = mkdtempSync(join(tmpdir(), 'graphlib-dist-exports-'));
writeFileSync(join(tmpDir, 'esm-check.mts'), esmCheck);
writeFileSync(join(tmpDir, 'cjs-check.cjs'), cjsCheck);
}, 60000);

afterAll(() => {
if (tmpDir) {
rmSync(tmpDir, {recursive: true, force: true});
}
});

it('ESM bundle exposes named exports under tsx', () => {
const out = execFileSync('npx', ['tsx', join(tmpDir, 'esm-check.mts')], {
cwd: repoRoot,
encoding: 'utf8',
shell: process.platform === 'win32',
});
expect(out).toContain('ESM OK');
}, 30000);

it('CJS bundle is requireable under node', () => {
const out = execFileSync(process.execPath, [join(tmpDir, 'cjs-check.cjs')], {
cwd: repoRoot,
encoding: 'utf8',
});
expect(out).toContain('CJS OK');
}, 30000);
});
Loading