Skip to content

Commit c00d68c

Browse files
authored
Add --save-config to persist flag-built configurations for reuse (#21)
1 parent 95c22cf commit c00d68c

3 files changed

Lines changed: 51 additions & 2 deletions

File tree

README.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Get a list of all games currently available on Xbox Game Pass (Console, PC or EA
1616
- [Configuration](#configuration)
1717
- [Examples](https://github.com/NikkelM/Game-Pass-API/tree/main/examples)
1818
- [Feedback](#feedback)
19+
- [Disclaimer](#disclaimer)
1920

2021
## Installation
2122

@@ -50,7 +51,7 @@ game-pass-api
5051
```
5152

5253
By default it reads `./config.json`; pass `--config <path>` to point at a different file.
53-
Run `game-pass-api --help` to see every command.
54+
Run `game-pass-api --help` to see every command, or `game-pass-api run --help` for the full list of flags.
5455

5556
You can also run without a `config.json` at all, building the configuration entirely from flags:
5657

@@ -59,8 +60,28 @@ game-pass-api --markets US,DE --platforms console,pc --properties productTitle,p
5960
```
6061

6162
Any option you omit uses its default.
63+
Add `--save-config` to also write the assembled configuration to a `config.json` (or `--save-config <path>`) so you can reuse or edit it later.
6264
Nested options such as images, pricing and user ratings are only available through a `config.json` or the wizard.
6365

66+
### Command-line flags
67+
68+
| Flag | Config key | Description |
69+
| --- | --- | --- |
70+
| `-c, --config <path>` | - | Path to a `config.json`. Defaults to `./config.json`. |
71+
| `--from <dir>` | - | Re-format previously-saved `completeGameProperties_*.json` files in `<dir>` instead of fetching (needs an earlier run with `keepCompleteProperties`). |
72+
| `-o, --out <dir>` | `outputDirectory` | Directory to write output files to. Default `output`. |
73+
| `--markets <codes>` | `markets` | Comma-separated market codes to fetch, e.g. `US,DE`. Enables flag-driven mode. |
74+
| `--platforms <list>` | `platformsToFetch` | Comma-separated platforms: `console,pc,eaPlay`. |
75+
| `--language <code>` | `language` | Language/locale for game properties, e.g. `en-us`. |
76+
| `--format <format>` | `outputFormat` | Output format: `array`, `productTitle`, `productId` or `0-indexed`. |
77+
| `--properties <list>` | `includedProperties` | Comma-separated properties to include: `productTitle,productId,developerName,publisherName,categories,storePage`. |
78+
| `--keep-complete` | `keepCompleteProperties` | Also keep the complete, unfiltered API response per platform and market. |
79+
| `--no-treat-empty-as-null` | `treatEmptyStringsAsNull` | Keep empty strings instead of converting them to `null`. |
80+
| `--save-config [path]` | - | Also write the assembled configuration to a file for reuse. Default `config.json`. |
81+
82+
Nested options (image types, pricing, user ratings, descriptions, release dates) are only available through a `config.json` or the wizard.
83+
The `init` command takes `-o, --output <path>` to choose where the wizard writes the configuration file (default `config.json`).
84+
6485
> Configuration files are validated against a JSON schema (`config.schema.json`, shipped with the package).
6586
> Add `"$schema": "config.schema.json"` to your `config.json`, with a copy of the schema next to it, and your editor will flag mistakes as you type.
6687

bin/cli.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import path from 'path';
55
import { fileURLToPath } from 'url';
66
import { Command } from 'commander';
77

8-
import { loadConfig, validateConfig } from '../js/utils.js';
8+
import { loadConfig, validateConfig, saveConfigToFile } from '../js/utils.js';
99
import { run } from '../js/gamePass.js';
1010
import { runWizard } from '../js/wizard.js';
1111
import { buildConfig, usedBuildingFlags } from '../js/cliConfig.js';
@@ -33,12 +33,16 @@ program
3333
.option('--properties <list>', 'comma-separated properties to include: productTitle,productId,developerName,publisherName,categories,storePage (flag-driven mode)')
3434
.option('--keep-complete', 'also keep the complete, unfiltered API response per platform and market (flag-driven mode)')
3535
.option('--no-treat-empty-as-null', 'keep empty strings instead of converting them to null (flag-driven mode)')
36+
.option('--save-config [path]', 'also write the assembled configuration to a file for reuse (default: config.json; flag-driven mode)')
3637
.addHelpText('after', '\nProvide a config.json (in the current directory or via --config), or build one from flags with --markets/--platforms/--properties etc. (unspecified options use their defaults).\nRun "game-pass-api init" to create a config interactively, or see the README and config.schema.json for every option.')
3738
.action(async (options, command) => {
3839
// Build the config entirely from flags when config-building flags are used (and no explicit --config file)
3940
if (!options.config && usedBuildingFlags(command)) {
4041
const config = buildConfig(options);
4142
validateConfig(config);
43+
if (options.saveConfig) {
44+
await saveConfigToFile(config, options.saveConfig === true ? 'config.json' : options.saveConfig);
45+
}
4246
await run(config, { fromDirectory: options.from });
4347
return;
4448
}

js/utils.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,36 @@ import jsonschema from 'jsonschema';
22
import fs from 'fs';
33
import path from 'path';
44
import { fileURLToPath } from 'url';
5+
import { confirm } from '@inquirer/prompts';
56

67
// The package root, so the shipped config schema is found no matter the working directory
78
const packageDir = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
89

910
export let CONFIG;
1011

12+
// ----- Saving a config -----
13+
14+
// Write a flag-built config to disk for reuse, stripping any secret fields so they are never persisted
15+
// Prompts before overwriting an existing file in an interactive shell; refuses to overwrite non-interactively
16+
export async function saveConfigToFile(config, outputPath, secretFields = []) {
17+
const toWrite = { ...config };
18+
for (const field of secretFields) {
19+
delete toWrite[field];
20+
}
21+
if (fs.existsSync(outputPath)) {
22+
if (!process.stdin.isTTY) {
23+
throw new Error(`"${outputPath}" already exists - remove it, or pass --save-config <path> with a different path.`);
24+
}
25+
const overwrite = await confirm({ message: `"${outputPath}" already exists. Overwrite it?`, default: false });
26+
if (!overwrite) {
27+
console.log('The existing configuration file was not changed.');
28+
return;
29+
}
30+
}
31+
fs.writeFileSync(outputPath, JSON.stringify(toWrite, null, 2));
32+
console.log(`Wrote configuration to "${outputPath}".`);
33+
}
34+
1135
// ----- Config -----
1236

1337
// Load a config from the given path, or discover ./config.json in the current directory, then validate it against the shipped schema

0 commit comments

Comments
 (0)