npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

dproject

v1.0.6

Published

Read MS Project (MSPDI XML) files in JavaScript and TypeScript. Zero-dependency, browser + Node.

Readme

DProject

Read MS Project (MSPDI XML) files in JavaScript and TypeScript. Zero dependencies. Works in the browser, in Node, and in workers. A clean-room JS implementation of the MSPDI reader the ecosystem has been missing for a decade.

License: MIT Status


Why

Today, if you want to read MS Project data in JavaScript, your options are:

| Option | Bundle | Trade-off | |---|---|---| | mpxj-wasm (Java→WASM) | ~10 MB | Heavy, slow startup | | Backend service (Java/Python MPXJ) | 0 (server-side) | Needs hosting | | Roll your own XML walker | ~?? | Fragile, no types |

DProject fills the gap: a small (~18 KB gzipped target), zero-dep, pure-JS library that turns an MSPDI XML string into a clean, JSON-serializable Project shape.

Scope (v1.0): MSPDI XML only. .mpp binary is a different beast — open in MS Project and Save As → XML to use DProject.

Peer comparison

Measured on sample-01.xml (887 KB, 121 tasks) — Node 24, M-class laptop:

| | Parse time (median) | Bundle (gzipped) | LOC for "list all task names" | Coverage | |---|---|---|---|---| | DProject | 22 ms | 7.5 KB | 2 lines | Full MSPDI + validate + serialize | | htmlparser2 (raw XML) | 25 ms | ~14 KB | ~150 lines | XML tree only | | mpxj-wasm (Java→WASM) | ~150 ms + multi-sec init | ~10 MB | ~8 lines | Full + .mpp binary |

DProject is ~10% faster than even raw XML parsing (htmlparser2) — while doing dramatically more work: full normalisation, type coercion, parent reconstruction, predecessor linking, baseline/calendar/extended-attribute extraction, and validation.

Run the benchmark yourself: node bench/peer-comparison.js.

Status

v1.0.0 — production-ready. All 10 quality gates green.

| # | Gate | Bar | Result | |---|---|---|---| | 1 | Unit tests | every layer covered | 165 cases across xml/coerce/edge/validator | | 2 | Real-world fixtures | 4 MSPDI samples parse | ✅ 121 + 23 + 22 + 33 tasks | | 3 | Edge cases | empty/malformed/encoding | ✅ 24 cases including BOM, CDATA, entities | | 4 | Schema validator | structural invariants | ✅ 10 error codes, all caught | | 6 | TypeScript surface | .d.ts matches runtime | ✅ 43 surface checks | | 7 | Bundle size | min ≤ 50 KB / gz ≤ 18 KB | ✅ 17 KB / 5.25 KB | | 8 | Performance | 1000 tasks < 100ms | ✅ 28 ms median (~35K tasks/sec) | | 9 | Memory | no leak across 100 parses | ✅ 0.1 MB growth | | 10 | Browser ↔ Node parity | byte-identical JSON | ✅ all 4 fixtures |

Total: 258 / 258 tests passing. Run npm test (or node tests/run-all.js).

Gate 5 (round-trip via writer) is deferred to v1.0.

🔜 v1.0 (GA) — round-trip writer (serialize()), peer benchmarks vs mpxj-wasm, calendars, npm publish.

Install

npm install dproject

Or drop into a browser as a single bundle:

<script src="dist/dproject.min.js"></script>
<script>
  const project = DProject.parse(xmlString);
</script>

Run npm run build to (re)generate dist/dproject.min.js and dist/dproject.min.js.gz.

Quick start

const DProject = require('dproject');

const project = DProject.parse(xmlString);

console.log(project.meta.title);           // "Project Management Workploan"
console.log(project.tasks.length);         // 121
console.log(project.tasks[0].duration);    // 38160 (minutes)
console.log(project.tasks[0].predecessors); // [ { predecessorUid, type, typeName, lag, ... } ]

Other entry points

// From a File / Blob (browser)
const project = await DProject.fromFile(fileInput.files[0]);

// From a URL (browser or Node 18+)
const project = await DProject.fromUrl('/schedules/q1.xml');

// From a path (Node)
const project = await DProject.fromFile('/abs/path/to/file.xml');

Project shape

{
  meta: {
    name, title, author, manager, company,
    startDate, finishDate, statusDate,
    currencySymbol, currencyCode, currencyDigits,
    minutesPerDay, minutesPerWeek, daysPerMonth,
    scheduleFromStart, calendarUid, ...
  },
  tasks: [
    {
      uid, id, name,
      outlineLevel, outlineNumber, parentUid,    // parent reconstructed for you
      summary, milestone, critical,
      start, finish,                             // ISO 8601 strings
      duration, work,                            // minutes (number)
      percentComplete, cost,
      predecessors: [
        { predecessorUid, type, typeName, lag, crossProject }
        //                ↑ 0/1/2/3   ↑ 'FF'|'FS'|'SF'|'SS'   ↑ minutes
      ],
      ...
    }
  ],
  resources:   [ { uid, id, name, type, maxUnits, standardRate, ... } ],
  assignments: [ { taskUid, resourceUid, units, work, cost, start, finish, ... } ]
}

The whole result is plain JSON — no Date objects, no classes, no circular refs. JSON.stringify(project) works.

Examples

Four runnable examples/ — JS users can node them directly, TS users get the same API with full types via import DProject from 'dproject':

| File | What it does | Run | |---|---|---| | extract-tasks.js | One-line summary per task: WBS · name · duration · start–finish · summary/milestone/critical flags | node examples/extract-tasks.js [file.xml] | | extract-deps.js | Print the dependency graph as edges (A → B (FS, +2d)); add --json for a machine-readable feed | node examples/extract-deps.js [--json] [file.xml] | | validate.js | Run the structural validator; errors grouped by code; exit 0/1/2 — CI-friendly | node examples/validate.js [file.xml] | | modify-and-write.js | Round-trip demo: parse → mutate (mark all milestones 100%) → serialize() → write .out.xml → re-parse to verify | node examples/modify-and-write.js [in.xml] [out.xml] |

Each example defaults to tests/fixtures/sample-01.xml so they work with zero arguments. Plus examples/viewer.html, a self-contained 48 KB browser viewer (drop-in <script>), and examples/consumer.ts, a TS surface compile check.

Design

DProject is built in four small layers, each with one job:

xml.js        ← pure-JS XML tokenizer (~250 LOC, no deps)
src/types/    ← coerce.js (int/bool/date/duration) + fields.js (declarative MSPDI mappings)
parser.js     ← walks XML tree → raw string fields
normalizer.js ← raw → typed Project + parent reconstruction
dproject.js   ← public API (parse / fromFile / fromUrl)

Adding an MSPDI field never touches the walker — just add a row to src/types/fields.js.

License

MIT — © 2026 Dharmesh Patel. See LICENSE and NOTICE.md.

Author

Built by Dharmesh Patel.