prettier-plugin-pdx-script
v0.1.0-rc.6
Published
Prettier plugin for formatting PDXScript (Paradox game script) files
Maintainers
Readme
prettier-plugin-pdx-script
A Prettier plugin for formatting PDXScript (Paradox game script) files. Powered by tree-sitter-pdx_script.
Overview
PDXScript is the scripting language used in Paradox Interactive games such as Victoria 3, Hearts of Iron IV, Stellaris, Europa Universalis IV, and Crusader Kings III. These scripts are distributed as .txt files with nested key-value and block structures.
This plugin provides automatic, consistent formatting for PDXScript files using Prettier. It uses a tree-sitter WASM parser to reliably parse PDXScript, then applies opinionated formatting rules. The parser is compiled from tree-sitter-pdx_script, a tree-sitter grammar for Paradox script files.
Installation
npm install --save-dev prettier-plugin-pdx-scriptPrettier will automatically discover the plugin. No additional configuration is needed.
Usage
# Format a single file
npx prettier --write path/to/file.txt
# Check formatting without writing
npx prettier --check path/to/file.txt
# Format all .txt files in a directory
npx prettier --write "path/to/scripts/**/*.txt"Formatting Rules
| Rule | Before | After |
| ------------ | ------------------------ | ---------------------------------------------------------- |
| Indentation | 4 spaces or mixed | Tabs (one per nesting level) |
| Empty blocks | my_decl = { } | my_decl = {} |
| Block values | key = { nested = val } | key = { nested = val} |
| Comments | # comment | # comment |
| Operators | Preserved as-is | =, >, < all supported |
Example
Input:
my_declaration={
key1= value1
key2 =value2
nested={
inner_key = "hello world"
}
# a comment
}
another_decl = { }Output:
my_declaration = {
key1 = value1
key2 = value2
nested = {
inner_key = "hello world"
}
# a comment
}
another_decl = {}Architecture
The plugin follows the standard Prettier plugin architecture with three components:
Input Text
│
▼
┌──────────────┐
│ Parser │ tree-sitter WASM parser + AST conversion
│ (index.ts) │ Converts tree-sitter SyntaxNodes → plain objects
└──────┬───────┘
│
▼
┌──────────────┐
│ Printer │ Walks the AST, produces Prettier Doc output
│(printer.ts) │ Handles each node type with specific formatting rules
└──────┬───────┘
│
▼
Formatted TextKey Design Decisions
Tree-to-plain-object conversion: Tree-sitter nodes use JavaScript getters (e.g.,
node.childrenis computed), but Prettier'spath.map/path.calltraverse node properties directly. TheconvertTree()function inindex.tsconverts the tree-sitter AST to plain objects that Prettier can navigate.Tab indentation: Paradox script files conventionally use tab indentation, which is preserved in this plugin.
Operator preservation: The
=,>, and<operators in key-value pairs are preserved from the source. They are anonymous tokens in tree-sitter (not innamedChildren) and are found by iteratingnode.children.
CJS / ESM Dual Support
This package ships both ESM and CJS entry points:
{
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"./dist/tree-sitter/tree-sitter-pdx_script.wasm": "./dist/tree-sitter/tree-sitter-pdx_script.wasm",
"./package.json": "./package.json"
}
}ESM consumers use import, CJS consumers use require() — no dynamic await import() needed.
The grammar WASM is also accessible via require.resolve("prettier-plugin-pdx-script/dist/tree-sitter/tree-sitter-pdx_script.wasm") for use with setGrammarBinary().
API
setGrammarBinary(loader)
Override the grammar WASM binary loader. Use this when bundling the plugin (e.g. in a VS Code extension) where the WASM file cannot be loaded from the plugin's package directory.
import { setGrammarBinary } from "prettier-plugin-pdx-script";
// Load from a bundled Uint8Array
setGrammarBinary(() => myWasmBinary);getGrammarBinary()
Get the current grammar binary loader function. Useful for wrapping the default loader or testing.
setLocateFile(fn)
Override the locateFile callback used to locate web-tree-sitter's own runtime WASM (tree-sitter.wasm). This is passed directly to web-tree-sitter's Parser.init({ locateFile }).
Use this when bundling the plugin and the default resolution (relative to scriptDir) does not point to the correct location.
import { setLocateFile } from "prettier-plugin-pdx-script";
setLocateFile((fileName, _scriptDir) => {
return path.join(__dirname, "wasm", fileName);
});Important: setLocateFile() must be called before any parse() invocation (same constraint as setGrammarBinary()).
getLocateFile()
Get the current locateFile callback. Useful for wrapping the default resolver or testing.
Bundling
When bundling this plugin (e.g. in a VS Code extension or webpack/esbuild build), the default WASM loading may fail. There are two WASM files to account for:
- tree-sitter runtime WASM —
tree-sitter.wasm, loaded byweb-tree-sitter'sParser.init()vialocateFile - grammar WASM —
tree-sitter-pdx_script.wasm, loaded by oursetGrammarBinary()callback
If either fails to resolve, the plugin will throw at init time.
Grammar WASM
Call setGrammarBinary() to supply the grammar WASM directly:
import { setGrammarBinary } from "prettier-plugin-pdx-script";
import wasmBinary from "./tree-sitter-pdx_script.wasm";
setGrammarBinary(() => wasmBinary);Runtime WASM (locateFile)
Call setLocateFile() to override how web-tree-sitter locates its own runtime WASM:
import { setLocateFile } from "prettier-plugin-pdx-script";
setLocateFile((fileName, _scriptDir) => {
return path.join(__dirname, "wasm", fileName);
});Both callbacks must be called before any parse() invocation.
Parser Caching
The tree-sitter parser is initialized once and cached for the lifetime of the module. Parser.init() and Language.load() are only called on the first parse() invocation; subsequent calls reuse the cached parser (~70ms saved per call).
File Structure
prettier-plugin-pdx-script/
├── index.ts # Plugin entry point: language definition, parser, API
├── printer.ts # Prettier printer: AST → Doc formatting
├── tsup.config.ts # tsup build config (dual CJS/ESM)
├── package.json # Package metadata and dependencies
├── README.md # This file
├── tree-sitter/
│ └── tree-sitter-pdx_script.wasm # Compiled tree-sitter WASM parser
└── dist/ # Compiled output (generated by build)
├── index.js # ESM entry point
├── index.cjs # CJS entry point
├── index.d.ts # TypeScript declarations (ESM)
├── index.d.cts # TypeScript declarations (CJS)
└── tree-sitter/
└── tree-sitter-pdx_script.wasmSource Files
| File | Purpose |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| index.ts | Exports languages, parsers, printers for Prettier. Defines the parser with caching, configurable WASM loading (setGrammarBinary, setLocateFile), and ESM/CJS __dirname resolution. |
| printer.ts | Exports the printers object with a print function that walks the AST and produces formatted Prettier Doc output. Handles all PDXScript node types. |
| tsup.config.ts | Build configuration for tsup — produces dual CJS/ESM output with TypeScript declarations. |
PDXScript Language Reference
Grammar
source_file → (declaration | comment)*
declaration → name = block
block → { (key_value | comment)* }
key_value → key (= | > | <) value
value → quoted_string | localisation_key | word | block
quoted_string → "..."
localisation_key → [word]
comment → #.*$
word → [^\s{}\[\]()=#"<>]+Node Types
| Node Type | Description | Example |
| ------------------ | ------------------------------------------------------------------- | --------------------- |
| source_file | Root node containing all declarations | — |
| declaration | Top-level named block | my_event = { ... } |
| block | Curly-brace delimited contents | { key = value } |
| key_value | Key-operator-value pair | key = value |
| value | Wrapper for one of: quoted string, localisation key, word, or block | — |
| quoted_string | Double-quoted string literal | "hello world" |
| localisation_key | Localisation reference | [MY_KEY] |
| word | Identifier, number, or unquoted value | my_var, 42 |
| comment | Line comment starting with # | # this is a comment |
Dependencies
- prettier
^3.8.1— Code formatter plugin API - web-tree-sitter
^0.26.7— WASM runtime for tree-sitter parsers
Building the WASM parser
The tree-sitter-pdx_script.wasm file is pre-compiled and included in the package. The grammar lives in a separate repository: tree-sitter-pdx_script.
To rebuild the WASM file (e.g., after modifying the grammar):
git clone https://github.com/Myriad4Link/tree-sitter-pdx_script
cd tree-sitter-pdx_script
npx tree-sitter build --wasm
cp tree-sitter-pdx_script.wasm /path/to/prettier-plugin-pdx-script/tree-sitter/Development
Build the compiled output (ESM + CJS via tsup):
bun run buildRun the test suite:
bun testLicense
MIT
