5eTools stores its data in JSON, which is a text-based filetype that follows a very particular structure. Although this structure is to enable a computer to easily parse and act on the data, there is, in general, no restriction on what data a JSON file can contain, or how that data is itself structured. The schema defines an additional structure: the structure that 5eTools understands.
You can think of a JSON schema like a form: it gives you a list of boxes to fill, tells you what kind of data each box should contain, lists the required and optional boxes, and so on. If you don’t follow the schema, then, at best 5eTools will simply not understand your file and nothing will happen; at worst, 5etools will interpret your file incorrectly, and cause other problems down the line.
The schema is an additional layer of verification on top of baseline JSON-validity, which you may have checked using tools such as JSONLint. The schema checks if you’re filling in the boxes correctly; JSONLint checks that you’re writing on the page at all.
It’s important to understand that, even if you don’t set up this schema, all files submitted to the public homebrew repository will be automatically checked against the schema. Your file must pass the schema before it will be accepted, so it’s much better to get it right first time.
The schema validates JSON types and structure. It doesn’t validate
@tag
styling or other in-line formatting.
There are two main ways to use the schema: live validation, which checks your work as you type in supported editors, and the test script. There’s some key differences, and both methods are useful.
The schema is stored in a seperate 5etools-utils NPM module.
Live validation is often the easiest way to ensure schema-validity.
With live validation, your editor will automatically find issues with your current file, and suggest corrections as you work on it. Here’s an example error message and its in-line suggestion.
Due to performance issues, live validation won’t do a complete verification of
entries
objects. Although live validation is advised while working, you should always do a complete verification after you’ve finished.If you’d like to use complete verification live regardless, change the schema directory from
brew-fast
tobrew
.
If you use Visual Studio Code, the most recommended method is to install the 5eTools Language Server extension. It automatically sets up your JSON for validation and provides helpful snippets for homebrew conversion.
This is the reccomended method that has the most features, the clearest feedback and the simplest setup.
Visual Studio Code offers two ways to set up schema-validation. You can either validate individual files, or you can validate an entire workspace.
Add the following property to the very top of your JSON. VSCode will automatically to validate only this file for you.
"$schema": "https://raw.githubusercontent.com/TheGiddyLimit/5etools-utils/master/schema/brew-fast/homebrew.json",
Please delete the
$schema
property before submitting to the homebrew repo.
You can alternatively configure VSCode to apply the homebrew schema to all JSON files in your workspace by editing your settings file.
settings.json
file.settings.json
file add the following code to apply the schema to all JSON files in your workspace. "json.schemas": [
{
"fileMatch": [
"*.json"
],
"url": "https://raw.githubusercontent.com/TheGiddyLimit/5etools-utils/master/schema/brew-fast/homebrew.json"
}
]
Other IDEs may support JSON validation (potentially through extensions). However, we don’t provide support for them here.
The following editors are known to lack support for live schema-validation:
The test script is guaranteed to validate a file against the schema completely. There are multiple ways to use it.
To use the test script in any form, you must first install Node.js and NPM (bundled with the installer), and add both to your
PATH
(default installer behaviour).
The test script will only tell you about the first error it finds in a file! You’ll need to re-run the script until you have no more errors.
Remember that programmers are strange and start counting at
0
not1
. If the error log tells you that you have a problem at/monster/3/alignment
this means that the fourthmonster
in the file has an issue with itsalignment
, not the third!
You can validate an individual file using npx
:
npx -p 5etools-utils test-json-brew "<file>"
, where <file>
is the relative or absolute path to your file.5etools-utils
), accept them by entering y.The file will be tested and any errors will be printed to the console. Note that some particular errors can result in very arcane cascade of errors; read them carefully to find the problem at hand.
Here’s an example error message.
If you have cloned the homebrew repo, you can use its own dedicated test scripts to validate multiple files at once.
A benefit of using this script is that it can also test for other common errors, such as mismatched curly brackets (a sign of erroneous @tag
usage). The downside is that it takes considerably longer to complete.
npm i
to install dependencies (you only need to do this once, but you should repeat it every few weeks in case of an update).npm test
.
npm test
technically runs a suite of different cleaners, indexers, and testers. If you want to test things slightly more separately (and quickly!), you can use the following commands instead:
npm run test:json
— validates the repo’s files against the JSON schemanpm run test:tags
— looks for mismatched curly brackets in the strings of the repo’s filesnpm run test:file-locations
— ensures that no files are in unexpected placesnpm run test:file-extensions
— ensures that no non-JSON files are present
Schema files follow a schema themselves and are laid out in a particular way. A number of examples are included below to aid understanding of what is allowed for 5etools.
$ref
Schemas can be very complex and may link back and forth between files. Some files may contain sections of code that are frequently reused by many files, while some files may contain content that is deliberately seperated out.
5etools has one main homebrew.json
file as the starting point for the whole schema. It acts like an index and points to other areas of the schema using $ref
. The homebrew.json
file defines the _meta
property locally, but links out to dedicated files for class
es, race
s, etc.
"class": {
"$ref": "class/class.json#/properties/class"
},
"race": {
"$ref": "races.json#/properties/race"
},
Meanwhile the class.json
and race.json
files both use $ref
to link to a standard set of code that defines skill proficiencies in the util.json
file.
"skills": {
"$ref": "../util.json#/definitions/skillProficiencies"
}
Some areas of the site have a list of values that are allowed like names of condition
s, or damage types.
Areas where you see "enum"
are enumerated lists, only the values in the list will be accepted.
In the homebrew schema you may see "examples"
which are lists of suggested values.
Where possible, use values from the suggested list, but you may also use custom values in "examples"
sections.
For example: below is a list of Warlock Pacts, note that you can define custom Pacts so "examples"
is used.
"pact": {
"type": "string",
"examples": [
"Chain",
"Tome",
"Blade",
"Talisman"
]
},
Contrast that with the "size"
array shown below which is handled specially across the site and so uses "enum"
.
"size": {
"type": "string",
"description": "F: Fine\n D: Diminutive\n T: Tiny\n S: Small\n M: Medium\n L: Large\n H: Huge\n G: Gargantuan\n C: Colossal\n V: Varies.",
"enum": [
"F",
"D",
"T",
"S",
"M",
"L",
"H",
"G",
"C",
"V"
]
},
The
"description"
property powers helpful dropdowns explaining what abbreviations mean or what a property does.
JSON stores data in a number of ways called "types"
. If you use the wrong sort of type for your data then the site will be unable to handle it and break.
[]
Arrays & {}
ObjectsEverything in JSON is either a piece of data, or a name that is used to refer to that data.
array
s which are recognisable by the square brackets []
contain multiple data points like text or numbers or even entire object
s like a monster
.
object
s contain named properties
like armor class, alignment or a creature’s name.
An array might be a list of conditions that a creature is immune to: |
|
Schema | Data |
---|---|
|
|
While an object might contain multiple properties like the average and formula of a monster's health: |
|
Schema | Data |
---|---|
|
|
string
, integer
, number
, boolean
& null
Within object
s or array
s individual data points need to be stored as particular types.
Below is a table of different types of variables.
Type | Schema | Data |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
null
is used when a property is not available but would otherwise be required.
It is not neccessary to usenull
for optional properties you would not otherwise have used.
We’ve discussed properties
individually but now let’s examine them as a group. Once you have an object
made up of a lot of propertes it can become quite complex.
We can define required
properties to let people know which properties they must provide. For example the sources
array in the _meta
block requires the "json"
, "abbreviation"
, "full"
& "version"
properties.
"required": [
"json",
"abbreviation",
"full",
"version"
]
Furthermore we want to restrict users from adding data that the site does nothing with, this can be done by specifying:
"additionalProperties": false
In some situations however, we want to allow users to supply new properties that we haven’t put in the schema but restrict them to being only the types we want. For example if we’re listing properties like:
"axe": true,
"sword": true
The user might define a new property denoting if an item is a firearm; this isn’t part of the core site but we want to let them use it by specifying "gun": true
.
In this case we would use the folowing schema to allow properties of that type:
"additionalProperties": {
"type": "boolean"
}
In some exceptional circumstances we might want to allow the user to create properties with a name in a particular format that has a more complex definition than just the type
. For this we would use patternProperties
where the name of the property is a regular expression that restricts the names. In this example any property must have a name that is a number with one or more digits.
"patternProperties": {
"\\d+": {
"type": "array",
"items": {"$ref": "#/definitions/spellRef"}
}
}
5eTools homebrew files are not interchangeable with the site (official) files, as they follow slightly different schemas. The homebrew schema is less restrictive than the site schema in some ways (e.g. to allow for homebrew mechanics like skills or item properties).
enum
rather than examples
arrayssubclassSpells
; some properties are also only used for the site, such as hasFluff
homebrew.json
file while the site applies individual schema files to each type of dataThe site and homebrew schema are both built from a proto-schema with custom syntax to allow it to output the homebrew and site files differently without compromising the flexibility or integrity of either. Detailed information on the build process for the schema can be found in the readme file in the 5etools-utils module at: schema-template\README.md
.
The homebrew schema can be built from the main site repo using the command:
node ./node/compile-schemas.js homebrew