docs: add a CONTRIBUTING file with basic instructions
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
# Contribute
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You will need Node.js installed. Install dependencies via `npm install`/`npm i`.
|
||||
|
||||
## Development
|
||||
|
||||
### Overview
|
||||
|
||||
There are three areas of importance when adding new entity descriptions or editing existing ones.
|
||||
|
||||
- `src/index.ts`: Register new entity descriptions in the `registeredEntityDescriptionCreators` object.
|
||||
- `src/entities`: This folder contains files with entity descriptors. You will find all existing ones in files directly within this folder. If you want to add a new entity descriptor, append to a fitting file or create a new one here.
|
||||
- `src/entities/partial`: This folder contains reusable helper functions for entity descriptors.
|
||||
|
||||
### `Reader` and `StdReader`
|
||||
|
||||
Sometimes, always having to explicitly pass functions and values a function depends on can be quite cumbersome. This is where `Reader<E, T>` comes in. Essentially, it serves as a _dependency injection_ mechanism. You can _ask_ (via `Reader.ask`) for the whole value of the environment (`E`) or extract specific parts via `Reader.asks`. Providing the values by calling the `run` method on a `Reader` instance will return a value of type `T`.
|
||||
|
||||
Since the environment values are quite similar across the project, there is a specific type `StdReader<T, K, E, AE, CE>` with an associated type `StdEnv<K, E, AE, CE>` that makes dealing with the same environment parts way easier and shorter. `StdReader` is in fact just a shortcut of combining `Reader` and `StdEnv`. But `StdEnv`’s type arguments need to be explained.
|
||||
|
||||
- `K`: A set of abbreviations of the keys required for this environment. For example, `"t"` corresponds to the `translate` function and `"ibi"` to the `getInstanceById` function. See the `EnvMapAbbr` type in `src/entities/partial/reader.ts` for all available abbreviations. If you need multiple values from `StdEnv`, separate them with pipes. For example `"t" | "ibi"` requires both `translate` and `getInstanceById`. If you use `ibi`, `ai` (`getAllInstances`), or `acibp` (`getAllChildInstancesByParent`), you will need to provide values for the type parameters `E`, `AE`, and `CE`, respectively. Type parameters you do not need should be set to `never`.
|
||||
- `E`: The set of entity names you want to request single instances from. If you need to get instances from multiple entities, separate them with pipes.
|
||||
- `AE`: The set of entity names you want to request all instances from. If you need to get instances from multiple entities, separate them with pipes.
|
||||
- `CE`: The set of child entity names you need to get instances from for a specific parent instance (e.g. getting all enhancements for a spell).
|
||||
|
||||
There are a lot of functions that wrap `Reader.asks` already, which makes working with `Reader`s way more straightforward.
|
||||
|
||||
#### Examples
|
||||
|
||||
Direct use for a translation string:
|
||||
|
||||
```ts
|
||||
const translationReader = Reader.asks((env: StdEnv<"t">) =>
|
||||
env.translate("Specific translation string"),
|
||||
)
|
||||
// somewhere up higher in the call hierarchy:
|
||||
const actualTranslation = translationReader.run({ translation })
|
||||
```
|
||||
|
||||
Translate with helper functions:
|
||||
|
||||
```ts
|
||||
const translationReader = translateR("Specific translation string")
|
||||
```
|
||||
|
||||
_Note:_ The `R` suffix for helper functions makes clear that it is returning a `Reader` instance instead of the final value.
|
||||
|
||||
#### Working with `Reader` instances
|
||||
|
||||
`Reader` instances need to be passed up to calling functions to that the uppermost function returns a `Reader` that contains the environment for all values it needs to produce.
|
||||
|
||||
You cannot modify a value from a `Reader` directly. Instead, use the `map` method to provide a function that modifies the value. To chain multiple `Reader`-producing functions together, use the `then` method. The function that gets passed to it receives the result from the previous reader and can return a new `Reader` with the same environment type. Use the `thenW` method to return a `Reader` with a different environment; the two environments will be merged.
|
||||
|
||||
To work with `Reader` instances in arrays, use `Reader.sequence` or `Reader.traverse`. The former one combines an array of `Reader`s into a single `Reader` of all the results of the array, the latter one applies a `Reader`-returning function to all elements of the array and then combines all results into a single `Reader`.
|
||||
|
||||
## Building
|
||||
|
||||
To build once or build whenever there are changes, use the following command:
|
||||
|
||||
- **One-time build:** `npm run build`
|
||||
- **Continuous building (watch mode):** `npm run watch`
|
||||
|
||||
## Testing
|
||||
|
||||
To check if the output matches your expectations, you can run the `testOutput.ts` script in the `scripts` folder. From the project root, call it the following way:
|
||||
|
||||
```sh
|
||||
node ./scripts/testOutput.ts -d ../path/to/data <locale> <entity> <instance>
|
||||
```
|
||||
|
||||
Replace values as appropriate:
|
||||
|
||||
- `../path/to/data` is the path to a local copy of the `elyukai/optolith-data` repository
|
||||
- `<locale>` is a locale identifier that exists in the database (e.g. `de-DE`)
|
||||
- `<entity>` is a valid entity name (e.g. `Spell`)
|
||||
- `<instance>` is a UUID of an instance of the specified entity
|
||||
|
||||
This will print a formatted textual representation to the terminal.
|
||||
Generated
+26
-26
@@ -627,9 +627,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/config-array/node_modules/brace-expansion": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.2.tgz",
|
||||
"integrity": "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==",
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
|
||||
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
@@ -637,7 +637,7 @@
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/config-array/node_modules/minimatch": {
|
||||
@@ -1119,9 +1119,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
|
||||
"integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
|
||||
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1332,9 +1332,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
|
||||
"integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2366,9 +2366,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/brace-expansion": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.2.tgz",
|
||||
"integrity": "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==",
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
|
||||
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
@@ -2376,7 +2376,7 @@
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/escape-string-regexp": {
|
||||
@@ -2956,9 +2956,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/handlebars": {
|
||||
"version": "4.7.8",
|
||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
|
||||
"integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==",
|
||||
"version": "4.7.9",
|
||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz",
|
||||
"integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -4010,9 +4010,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
|
||||
"integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
|
||||
"version": "8.4.1",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.1.tgz",
|
||||
"integrity": "sha512-fvU78fIjZ+SBM9YwCknCvKOUKkLVqtWDVctl0s7xIqfmfb38t2TT4ZU2gHm+Z8xGwgW+QWEU3oQSAzIbo89Ggw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@@ -4050,9 +4050,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -5272,9 +5272,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.8.2",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz",
|
||||
"integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
|
||||
"version": "2.8.3",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
|
||||
"integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
|
||||
+30
-21
@@ -6,7 +6,7 @@ import type { MessageValue } from "messageformat/functions"
|
||||
import { findPackageJSON } from "node:module"
|
||||
import { dirname, join } from "node:path"
|
||||
import { argv } from "node:process"
|
||||
import { styleText, type InspectColor } from "node:util"
|
||||
import { parseArgs, styleText, type InspectColor, type ParseArgsOptionsConfig } from "node:util"
|
||||
import { schema } from "optolith-database-schema"
|
||||
import { createCache, type IdMap as CacheIdMap } from "optolith-database-schema/cache"
|
||||
import { TSONDB } from "tsondb"
|
||||
@@ -20,27 +20,26 @@ import {
|
||||
type TableEntityDescriptionSection,
|
||||
} from "../lib/index.js"
|
||||
|
||||
const dataRootPath = join(
|
||||
dirname(findPackageJSON(import.meta.url) ?? import.meta.filename),
|
||||
"..",
|
||||
"client",
|
||||
"src",
|
||||
"database",
|
||||
"contents",
|
||||
"data",
|
||||
)
|
||||
const options = {
|
||||
data: {
|
||||
type: "string",
|
||||
short: "d",
|
||||
default: join(
|
||||
dirname(findPackageJSON(import.meta.url) ?? import.meta.filename),
|
||||
"..",
|
||||
"client",
|
||||
"src",
|
||||
"database",
|
||||
"contents",
|
||||
"data",
|
||||
),
|
||||
},
|
||||
} satisfies ParseArgsOptionsConfig
|
||||
|
||||
const db = await TSONDB.create({
|
||||
schema,
|
||||
dataRootPath,
|
||||
locales: ["de-DE"],
|
||||
})
|
||||
|
||||
const [localeId, entity, id] = argv.slice(2)
|
||||
|
||||
if (!entity || !db.schema.isEntityName(entity) || !isSupportedEntity(entity)) {
|
||||
throw new Error("Invalid entity name")
|
||||
}
|
||||
const {
|
||||
values: { data: dataRootPath },
|
||||
positionals: [localeId, entity, id],
|
||||
} = parseArgs({ args: argv.slice(2), options, allowPositionals: true })
|
||||
|
||||
if (localeId === undefined) {
|
||||
throw new Error("No locale provided")
|
||||
@@ -50,6 +49,16 @@ if (!id) {
|
||||
throw new Error("No ID provided")
|
||||
}
|
||||
|
||||
const db = await TSONDB.create({
|
||||
schema,
|
||||
dataRootPath,
|
||||
locales: [localeId],
|
||||
})
|
||||
|
||||
if (!entity || !db.schema.isEntityName(entity) || !isSupportedEntity(entity)) {
|
||||
throw new Error("Invalid entity name")
|
||||
}
|
||||
|
||||
const localeInstance = db.getInstanceOfEntityById("Locale", localeId)
|
||||
|
||||
if (localeInstance === undefined) {
|
||||
|
||||
Reference in New Issue
Block a user