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

bsdata40k-to-json

v1.0.1

Published

Convert BSData BattleScribe XML files (.cat/.gst) to JSON — raw or curated

Readme

bsdata40k-to-json

Convert BSData BattleScribe XML for Warhammer 40,000 into JSON. Turn army list data (factions, units, weapons, abilities, points) from the community-maintained BSData repos into two output formats:

  • Raw — Faithful 1:1 XML-to-JSON conversion preserving all BattleScribe elements, attributes, and list-builder logic
  • Curated — Clean, game-ready JSON: datasheet stats (M, T, SV, W, LD, OC), ranged/melee weapon profiles, abilities, keywords, transport capacity, leader attachments, enhancements, and points per faction

Built for Warhammer 40,000 (40k) 10th edition data. Use the output for list-building apps, points calculators, roster viewers, or any tool that needs structured 40k game data.

Install

npm install bsdata40k-to-json

Or install globally for CLI access:

npm install -g bsdata40k-to-json

CLI Usage

Pass the input directory (BSData repo) and output directory as two arguments:

bsdata40k-to-json ./wh40k-10e ./output

Options

| Argument / Flag | Description | |---|---| | <input-dir> | Directory containing .cat and .gst files (required) | | <output-dir> | Directory to write JSON output (required) | | --raw-only | Only produce raw (1:1 XML-to-JSON) output | | --curated-only | Only produce curated game-data output | | --help, -h | Show help message |

Development scripts

| Command | Description | |---|---| | npm run convert -- <input-dir> <output-dir> | Run both pipelines via tsx | | npm run convert:raw -- <input-dir> <output-dir> | Raw XML-to-JSON only | | npm run convert:curated -- <input-dir> <output-dir> | Curated extraction only | | npm run build | Compile TypeScript to dist/ |

Library Usage

import { convert } from "bsdata40k-to-json";

const result = await convert({
  inputDir: "./wh40k-10e",
  outputDir: "./output",
  curatedOnly: true,
});

console.log(`Converted ${result.factionCount} factions`);

Individual extractors are also available:

import {
  convertFileToRawJson,
  extractGameSystem,
  extractCatalogue,
  discoverFiles,
} from "bsdata40k-to-json";

const { gstFiles, catFiles } = await discoverFiles("./wh40k-10e");
const rawJson = await convertFileToRawJson(gstFiles[0]);
const gameSystem = extractGameSystem(rawJson);

All TypeScript types are exported:

import type {
  CuratedFaction,
  CuratedUnit,
  WeaponProfile,
  Enhancement,
  CuratedGameSystem,
  ConvertOptions,
  ConvertResult,
} from "bsdata40k-to-json";

Output Structure

<output-dir>/
  raw/                          # 1:1 XML-to-JSON
    game-system.json            # Warhammer 40,000.gst (core rules, profile types)
    necrons.json                # One file per faction catalogue (.cat)
    imperium-space-marines.json
    ...

  curated/                      # Clean game data
    game-system.json            # Shared rules (e.g. Blast, Devastating Wounds), profile types, categories
    factions/
      necrons.json
      imperium-space-marines.json
      ...
    index.json                  # Faction manifest with unit counts

What’s in the curated output (40k terms)

  • Factions — One JSON file per 40k faction (e.g. Necrons, Space Marines, Tyranids), with name, catalogueName, and all units/enhancements for that army.
  • Units — Full datasheet-style data: Movement (M), Toughness (T), Save (SV), Wounds (W), Leadership (LD), Objective Control (OC), plus keywords (Infantry, Character, etc.) and faction keywords.
  • Weapons — Ranged and melee weapon profiles with Range, Attacks (A), Ballistic Skill (BS) or Weapon Skill (WS), Strength (S), Armour Penetration (AP), Damage (D), and weapon keywords (e.g. Devastating Wounds, Blast).
  • Abilities — Named abilities and their rules text (e.g. Master Chronomancer, Oath of Moment).
  • Enhancements — Detachment enhancements with name, points cost, and description; optional detachment when applicable.
  • Leader attachments — Which units a character can lead (e.g. Orikan the Diviner → Immortals, Necron Warriors).

Curated JSON Format (example: Necrons)

Each faction file contains units with full 40k-style datasheets:

{
  "id": "b654-a18a-ea1-3bf2",
  "name": "Necrons",
  "catalogueName": "Xenos - Necrons",
  "units": [
    {
      "id": "ba86-eaad-5396-fc07",
      "name": "Orikan the Diviner",
      "points": 80,
      "keywords": ["Infantry", "Character", "Epic Hero", "Cryptek"],
      "factionKeywords": ["Necrons"],
      "stats": { "M": "5\"", "T": "4", "SV": "4+", "W": "4", "LD": "6+", "OC": "1" },
      "rangedWeapons": [],
      "meleeWeapons": [
        {
          "name": "Staff of Tomorrow",
          "range": "Melee",
          "A": "2",
          "WS": "3+",
          "S": "4",
          "AP": "-3",
          "D": "D3",
          "keywords": ["Devastating Wounds"]
        }
      ],
      "abilities": [
        { "name": "Master Chronomancer", "description": "While this model is leading a unit, models in that unit have a 4+ invulnerable save." }
      ],
      "leader": ["Immortals", "Necron Warriors"]
    }
  ],
  "enhancements": [
    {
      "name": "Veil of Darkness",
      "points": 20,
      "description": "NECRONS model only. Once per battle...",
      "detachment": "Awakened Dynasty"
    }
  ]
}

Project Structure

src/
  cli.ts                         # CLI entry point (arg parsing)
  index.ts                       # Library entry point (programmatic API + re-exports)
  raw-converter.ts               # Faithful XML-to-JSON using fast-xml-parser
  utils.ts                       # File discovery, slugification, helpers
  curated/
    types.ts                     # TypeScript interfaces for output shapes
    extract-game-system.ts       # .gst extractor (rules, profile types, categories)
    extract-catalogue.ts         # .cat extractor (units, weapons, abilities, enhancements)

How It Works

  1. Discovery — Scans the input directory for the 40k game system (.gst) and faction catalogues (.cat).
  2. Raw conversion — Parses each XML file with fast-xml-parser, preserving attributes (prefixed with @_) and forcing arrays for repeatable elements.
  3. Curated extraction — Walks the parsed XML tree to extract 40k game data:
    • Resolves entryLink cross-references (shared units, wargear, weapons) by building an ID lookup map
    • Recursively collects profiles from nested selectionEntry, selectionEntryGroup, and entryLink elements
    • Deduplicates weapons by name
    • Parses leader attachment targets from ability descriptions (e.g. “This model can be attached to…”)
    • Separates faction keywords (e.g. Faction: Necrons) from unit keywords (Infantry, Character, etc.)

Data Source (Warhammer 40,000)

Input files come from the BSData community-maintained Warhammer 40,000 data. For 10th edition use the BSData/wh40k-10e repository:

git clone https://github.com/BSData/wh40k-10e.git
bsdata40k-to-json ./wh40k-10e ./output

The repo contains the core game system (Warhammer 40,000.gst) and one catalogue (.cat) per faction (Space Marines, Necrons, Tyranids, etc.). Download or clone the repo and pass its path as the first argument.

License

MIT