Skip to content

Commit 01d1108

Browse files
committed
Add package and config commands
1 parent c25b9d6 commit 01d1108

File tree

18 files changed

+1015
-0
lines changed

18 files changed

+1015
-0
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
2+
# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
3+
4+
name: Node.js Package
5+
6+
on:
7+
release:
8+
types: [created]
9+
10+
jobs:
11+
build:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v3
15+
- uses: actions/setup-node@v3
16+
with:
17+
node-version: 16
18+
- run: npm ci
19+
20+
publish-gpr:
21+
needs: build
22+
runs-on: ubuntu-latest
23+
permissions:
24+
contents: read
25+
packages: write
26+
steps:
27+
- uses: actions/checkout@v3
28+
- uses: actions/setup-node@v3
29+
with:
30+
node-version: 16
31+
registry-url: https://npm.pkg.github.com/
32+
- run: npm ci
33+
- run: npm publish
34+
env:
35+
NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
node_modules/

.idea/.gitignore

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/foundryvtt-cli.iml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/modules.xml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/vcs.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

commands/configuration.mjs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import Config from "../config.mjs";
2+
3+
export function getCommand() {
4+
return {
5+
command: "configure [action] [key] [value]",
6+
describe: "Manage configuration",
7+
builder: (yargs) => {
8+
yargs.positional("action", {
9+
describe: "The action to perform",
10+
type: "string",
11+
choices: ["get", "set"]
12+
})
13+
.positional("key", {
14+
describe: "The configuration key",
15+
type: "string"
16+
})
17+
.positional("value", {
18+
describe: "The configuration value",
19+
type: "string"
20+
});
21+
},
22+
handler: async (argv) => {
23+
console.log("configure handler", argv);
24+
25+
// Handle actions
26+
switch ( argv.action ) {
27+
case "get": {
28+
console.log(Config.instance.get(argv.key));
29+
break;
30+
}
31+
case "set": {
32+
Config.instance.set(argv.key, argv.value);
33+
console.log(`Set ${argv.key} to ${argv.value}`);
34+
break;
35+
}
36+
default: {
37+
// Output the current configuration
38+
console.log(Config.instance.getAll());
39+
break;
40+
}
41+
}
42+
}
43+
}
44+
}

commands/package.mjs

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
import Config from "../config.mjs";
2+
import {ClassicLevel} from "classic-level";
3+
import yaml from "js-yaml";
4+
import path from "path";
5+
import fs from "fs";
6+
7+
export function getCommand() {
8+
let currentPackageId = Config.instance.get("currentPackageId");
9+
let currentPackageType = Config.instance.get("currentPackageType");
10+
11+
return {
12+
command: "package [action]",
13+
describe: "Manage packages",
14+
builder: (yargs) => {
15+
yargs.positional("action", {
16+
describe: "The action to perform",
17+
type: "string",
18+
choices: ["set", "clear", "write", "pack"]
19+
});
20+
21+
// If no currentPackageId is set and the action is not "clear", require an `id` option to be set
22+
yargs.option("id", {
23+
describe: "The package ID",
24+
type: "string",
25+
demandOption: currentPackageId === null,
26+
});
27+
28+
yargs.option("type", {
29+
describe: "The package type",
30+
type: "string",
31+
choices: ["Module", "System", "World"],
32+
demandOption: currentPackageType === null,
33+
});
34+
35+
yargs.option("documentName", {
36+
alias: "n",
37+
describe: "The document name, for Pack based Actions.",
38+
type: "string",
39+
choices: [ "Actor", "Card", "Item", "Journal", "Playlist", "Scene", "Table" ]
40+
});
41+
42+
yargs.option("directory", {
43+
alias: "d",
44+
describe: "The directory to serialize to / from, for Pack based Actions.",
45+
type: "string"
46+
});
47+
},
48+
handler: async (argv) => {
49+
console.log("package handler", argv);
50+
51+
if ( argv.id ) {
52+
currentPackageId = argv.id;
53+
}
54+
if ( argv.type ) {
55+
currentPackageType = argv.type;
56+
}
57+
58+
// Handle actions
59+
switch ( argv.action ) {
60+
case "set": {
61+
_handleSet();
62+
break;
63+
}
64+
65+
case "clear": {
66+
_handleClear();
67+
break;
68+
}
69+
70+
case "write": {
71+
await _handleWrite(argv);
72+
break;
73+
}
74+
75+
case "pack": {
76+
await _handlePack(argv);
77+
break;
78+
}
79+
80+
default: {
81+
console.log(`Currently in ${currentPackageType} ${currentPackageId}`);
82+
break;
83+
}
84+
}
85+
}
86+
}
87+
88+
/* -------------------------------------------- */
89+
90+
/**
91+
* Set the current package ID and type
92+
* @private
93+
*/
94+
function _handleSet() {
95+
Config.instance.set("currentPackageId", currentPackageId);
96+
Config.instance.set("currentPackageType", currentPackageType);
97+
console.log(`Swapped to ${currentPackageType} ${currentPackageId}`);
98+
}
99+
100+
/* -------------------------------------------- */
101+
102+
/**
103+
* Clear the current package ID and type
104+
* @private
105+
*/
106+
function _handleClear() {
107+
currentPackageId = null;
108+
currentPackageType = null;
109+
Config.instance.set("currentPackageId", currentPackageId);
110+
Config.instance.set("currentPackageType", currentPackageType);
111+
console.log("Cleared current Package");
112+
}
113+
114+
/* -------------------------------------------- */
115+
116+
function normalizePath(pathToNormalize) {
117+
return path.normalize(pathToNormalize).split(path.sep).join(path.posix.sep);
118+
}
119+
120+
/* -------------------------------------------- */
121+
122+
/**
123+
* Load a pack from a directory and write the db entries to YAML files
124+
* @param argv
125+
* @returns {Promise<void>}
126+
* @private
127+
*/
128+
async function _handleWrite(argv) {
129+
const typeDir = currentPackageType.toLowerCase() + "s";
130+
131+
if ( !argv.documentName ) {
132+
console.error("No documentName provided for the `writePack` action. Try again with `-n <documentName>`.");
133+
return;
134+
}
135+
136+
const documentDir = argv.documentName.toLowerCase() + "s";
137+
const dataPath = Config.instance.get("dataPath");
138+
if ( !dataPath ) {
139+
console.error("No dataPath configured. Call `configure set dataPath <path>` first.");
140+
return;
141+
}
142+
143+
const packDir = normalizePath(`${dataPath}/${typeDir}/${currentPackageId}/data/${documentDir}`);
144+
const outputDir = normalizePath(`${argv.directory ?? `./${typeDir}/${currentPackageId}`}/${documentDir}`);
145+
console.log(`Writing pack "${packDir}" to "${outputDir}"`);
146+
147+
try {
148+
// Load the directory as a ClassicLevel db
149+
const db = new ClassicLevel(packDir, {keyEncoding: "utf8", valueEncoding: "json"});
150+
151+
// Iterate over all entries in the db, writing them as individual YAML files
152+
if (!fs.existsSync(outputDir)) {
153+
fs.mkdirSync(outputDir, {recursive: true});
154+
}
155+
for await (const [key, value] of db.iterator()) {
156+
const name = value.name ? `${value.name.toLowerCase().replaceAll(" ", "_")}_${value._id}` : key;
157+
const fileName = `${outputDir}/${name}.yml`;
158+
;
159+
fs.writeFileSync(fileName, yaml.dump(value));
160+
console.log(`Wrote ${fileName}`);
161+
}
162+
163+
await db.close();
164+
}
165+
catch (err) {
166+
console.error(err);
167+
}
168+
}
169+
170+
/* -------------------------------------------- */
171+
172+
/**
173+
* Read YAML files from a directory and write them to a pack db
174+
* @param argv
175+
* @returns {Promise<void>}
176+
* @private
177+
*/
178+
async function _handlePack(argv) {
179+
const typeDir = currentPackageType.toLowerCase() + "s";
180+
181+
if ( !argv.documentName ) {
182+
console.error("No documentName provided for the `writePack` action. Try again with `-n <documentName>`.");
183+
return;
184+
}
185+
186+
const documentDir = argv.documentName.toLowerCase() + "s";
187+
const dataPath = Config.instance.get("dataPath");
188+
if ( !dataPath ) {
189+
console.error("No dataPath configured. Call `configure set dataPath <path>` first.");
190+
return;
191+
}
192+
const packDir = normalizePath(`${dataPath}/${typeDir}/${currentPackageId}/data/${documentDir}`);
193+
const inputDir = normalizePath(`${argv.directory ?? `./${typeDir}/${currentPackageId}`}/${documentDir}`);
194+
console.log(`Packing "${inputDir}" into pack "${packDir}"`);
195+
196+
try {
197+
// Load the directory as a ClassicLevel db
198+
const db = new ClassicLevel(packDir, {keyEncoding: "utf8", valueEncoding: "json"});
199+
200+
// Iterate over all YAML files in the input directory, writing them to the db
201+
const files = fs.readdirSync(inputDir);
202+
for ( const file of files ) {
203+
const value = yaml.load(fs.readFileSync(path.join(inputDir, file)));
204+
await db.put(value._id, value);
205+
console.log(`Packed ${value._id}${value.name ? ` (${value.name})` : ""}`);
206+
}
207+
}
208+
catch (err) {
209+
console.error(err);
210+
}
211+
}
212+
}

config.example.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
dataPath: C:\Users\Example\AppData\Local\FoundryVTT\Data
2+
currentPackageId: example-id
3+
currentPackageType: World

config.mjs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import fs from "fs";
2+
import yaml from "js-yaml";
3+
4+
/**
5+
* Manages the configuration of the CLI. Stored as config.yml
6+
*/
7+
export default class Config {
8+
9+
static #instance = null;
10+
11+
/* -------------------------------------------- */
12+
13+
static get instance() {
14+
if (!this.#instance) {
15+
this.#instance = new Config();
16+
}
17+
return this.#instance;
18+
}
19+
20+
/* -------------------------------------------- */
21+
22+
constructor() {
23+
this.#config = yaml.load(fs.readFileSync("./config.yml", "utf8"));
24+
}
25+
26+
/* -------------------------------------------- */
27+
28+
#config = {};
29+
30+
/* -------------------------------------------- */
31+
32+
getAll() {
33+
return this.#config;
34+
}
35+
36+
/* -------------------------------------------- */
37+
38+
get(key) {
39+
return this.#config[key];
40+
}
41+
42+
/* -------------------------------------------- */
43+
44+
set(key, value) {
45+
this.#config[key] = value;
46+
47+
// Write to disk
48+
this.#writeConfig();
49+
}
50+
51+
/* -------------------------------------------- */
52+
53+
#writeConfig() {
54+
// Write to disk
55+
fs.writeFileSync("./config.yml", yaml.dump(this.#config));
56+
}
57+
}

data/worlds/black-flag/data/actors/000003.log

Whitespace-only changes.

0 commit comments

Comments
 (0)