diff --git a/README.md b/README.md index 6ed79c9..551e897 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Get a list of all games currently available on Xbox Game Pass (Console, PC or EA - [Configuration](#configuration) - [Examples](https://github.com/NikkelM/Game-Pass-API/tree/main/examples) - [Feedback](#feedback) +- [Disclaimer](#disclaimer) ## Installation @@ -50,7 +51,7 @@ game-pass-api ``` By default it reads `./config.json`; pass `--config ` to point at a different file. -Run `game-pass-api --help` to see every command. +Run `game-pass-api --help` to see every command, or `game-pass-api run --help` for the full list of flags. You can also run without a `config.json` at all, building the configuration entirely from flags: @@ -59,8 +60,28 @@ game-pass-api --markets US,DE --platforms console,pc --properties productTitle,p ``` Any option you omit uses its default. +Add `--save-config` to also write the assembled configuration to a `config.json` (or `--save-config `) so you can reuse or edit it later. Nested options such as images, pricing and user ratings are only available through a `config.json` or the wizard. +### Command-line flags + +| Flag | Config key | Description | +| --- | --- | --- | +| `-c, --config ` | - | Path to a `config.json`. Defaults to `./config.json`. | +| `--from ` | - | Re-format previously-saved `completeGameProperties_*.json` files in `` instead of fetching (needs an earlier run with `keepCompleteProperties`). | +| `-o, --out ` | `outputDirectory` | Directory to write output files to. Default `output`. | +| `--markets ` | `markets` | Comma-separated market codes to fetch, e.g. `US,DE`. Enables flag-driven mode. | +| `--platforms ` | `platformsToFetch` | Comma-separated platforms: `console,pc,eaPlay`. | +| `--language ` | `language` | Language/locale for game properties, e.g. `en-us`. | +| `--format ` | `outputFormat` | Output format: `array`, `productTitle`, `productId` or `0-indexed`. | +| `--properties ` | `includedProperties` | Comma-separated properties to include: `productTitle,productId,developerName,publisherName,categories,storePage`. | +| `--keep-complete` | `keepCompleteProperties` | Also keep the complete, unfiltered API response per platform and market. | +| `--no-treat-empty-as-null` | `treatEmptyStringsAsNull` | Keep empty strings instead of converting them to `null`. | +| `--save-config [path]` | - | Also write the assembled configuration to a file for reuse. Default `config.json`. | + +Nested options (image types, pricing, user ratings, descriptions, release dates) are only available through a `config.json` or the wizard. +The `init` command takes `-o, --output ` to choose where the wizard writes the configuration file (default `config.json`). + > Configuration files are validated against a JSON schema (`config.schema.json`, shipped with the package). > 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. diff --git a/bin/cli.js b/bin/cli.js index bbdb74b..f22a917 100644 --- a/bin/cli.js +++ b/bin/cli.js @@ -5,7 +5,7 @@ import path from 'path'; import { fileURLToPath } from 'url'; import { Command } from 'commander'; -import { loadConfig, validateConfig } from '../js/utils.js'; +import { loadConfig, validateConfig, saveConfigToFile } from '../js/utils.js'; import { run } from '../js/gamePass.js'; import { runWizard } from '../js/wizard.js'; import { buildConfig, usedBuildingFlags } from '../js/cliConfig.js'; @@ -33,12 +33,16 @@ program .option('--properties ', 'comma-separated properties to include: productTitle,productId,developerName,publisherName,categories,storePage (flag-driven mode)') .option('--keep-complete', 'also keep the complete, unfiltered API response per platform and market (flag-driven mode)') .option('--no-treat-empty-as-null', 'keep empty strings instead of converting them to null (flag-driven mode)') + .option('--save-config [path]', 'also write the assembled configuration to a file for reuse (default: config.json; flag-driven mode)') .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.') .action(async (options, command) => { // Build the config entirely from flags when config-building flags are used (and no explicit --config file) if (!options.config && usedBuildingFlags(command)) { const config = buildConfig(options); validateConfig(config); + if (options.saveConfig) { + await saveConfigToFile(config, options.saveConfig === true ? 'config.json' : options.saveConfig); + } await run(config, { fromDirectory: options.from }); return; } diff --git a/js/utils.js b/js/utils.js index 0c46e64..9aacdb5 100644 --- a/js/utils.js +++ b/js/utils.js @@ -2,12 +2,36 @@ import jsonschema from 'jsonschema'; import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; +import { confirm } from '@inquirer/prompts'; // The package root, so the shipped config schema is found no matter the working directory const packageDir = path.dirname(path.dirname(fileURLToPath(import.meta.url))); export let CONFIG; +// ----- Saving a config ----- + +// Write a flag-built config to disk for reuse, stripping any secret fields so they are never persisted +// Prompts before overwriting an existing file in an interactive shell; refuses to overwrite non-interactively +export async function saveConfigToFile(config, outputPath, secretFields = []) { + const toWrite = { ...config }; + for (const field of secretFields) { + delete toWrite[field]; + } + if (fs.existsSync(outputPath)) { + if (!process.stdin.isTTY) { + throw new Error(`"${outputPath}" already exists - remove it, or pass --save-config with a different path.`); + } + const overwrite = await confirm({ message: `"${outputPath}" already exists. Overwrite it?`, default: false }); + if (!overwrite) { + console.log('The existing configuration file was not changed.'); + return; + } + } + fs.writeFileSync(outputPath, JSON.stringify(toWrite, null, 2)); + console.log(`Wrote configuration to "${outputPath}".`); +} + // ----- Config ----- // Load a config from the given path, or discover ./config.json in the current directory, then validate it against the shipped schema