diff --git a/README.md b/README.md index 41317d4..c89daed 100755 --- a/README.md +++ b/README.md @@ -203,7 +203,8 @@ Instantiates middleware. See an [example](https://github.com/firebase/superstati * `options` - Optional configuration: * `fallthrough` - When `false`, render a 404 page from within Superstatic rather than calling through to the next middleware. Defaults to `true`. - * `config` - A file path to your application's configuration file (see [Configuration](#configuration)) or an object containing your application's configuration. If an object is provided, it will be merged into existing config in a `superstatic.json`. + * `config` - A file path to your application's configuration file (see [Configuration](#configuration)) or an object containing your application's configuration. If an object is provided, it will be merged into existing config in a `superstatic.json` or `firebase.json` unless `mergeConfig` is set to `false`. + * `mergeConfig` - When `false`, the config object you provide is used as-is without being merged with any `superstatic.json` or `firebase.json` found on disk. Defaults to `true`. Only applies when `config` is a plain object. * `protect` - Adds HTTP basic auth. Example: `username:password` * `env`- A file path your application's environment variables file or an object containing values that are made available at the urls `/__/env.json` and `/__/env.js`. See the documentation detail on [environment variables](http://docs.firebase.com/guides/environment-variables). * `cwd` - The current working directory to set as the root. Your application's `public` configuration option will be used relative to this. diff --git a/src/loaders/config-file.js b/src/loaders/config-file.js index 9b158a8..f6a12ab 100644 --- a/src/loaders/config-file.js +++ b/src/loaders/config-file.js @@ -27,7 +27,7 @@ const { isPlainObject } = require("../utils/objectutils"); const CONFIG_FILE = ["superstatic.json", "firebase.json"]; -module.exports = function (filename) { +module.exports = function (filename, mergeConfig = true) { if (typeof filename === "function") { return filename; } @@ -35,14 +35,17 @@ module.exports = function (filename) { filename = filename ?? CONFIG_FILE; let configObject = {}; + let configObjectProvided = false; let config = {}; // From custom config data passed in try { configObject = JSON.parse(filename); + configObjectProvided = true; } catch { if (isPlainObject(filename)) { configObject = filename; + configObjectProvided = true; filename = CONFIG_FILE; } } @@ -71,5 +74,5 @@ module.exports = function (filename) { // Passing an object as the config value merges // the config data - return { ...config, ...configObject }; + return configObjectProvided && !mergeConfig ? configObject : { ...config, ...configObject }; }; diff --git a/src/options.ts b/src/options.ts index 61f265b..e3402ff 100644 --- a/src/options.ts +++ b/src/options.ts @@ -3,6 +3,7 @@ import { Configuration } from "./config"; export interface MiddlewareOptions { fallthrough?: boolean; + mergeConfig?: boolean; config?: string | Configuration; protect?: string; env?: string | Record; diff --git a/src/superstatic.js b/src/superstatic.js index f18e042..0de4290 100644 --- a/src/superstatic.js +++ b/src/superstatic.js @@ -51,7 +51,7 @@ const superstatic = function (spec = {}) { // Load data /** @type {Configuration} */ - const config = (spec.config = loadConfigFile(spec.config)); + const config = (spec.config = loadConfigFile(spec.config, spec.mergeConfig)); config.errorPage = config.errorPage ?? "/404.html"; // Set up provider diff --git a/test/unit/loaders/config-file.spec.js b/test/unit/loaders/config-file.spec.js index dbba00f..8024cc6 100644 --- a/test/unit/loaders/config-file.spec.js +++ b/test/unit/loaders/config-file.spec.js @@ -77,6 +77,48 @@ describe("loading config files", () => { done(); }); + describe("mergeConfig: false", () => { + it("returns object as-is without merging with superstatic.json", async () => { + await fs.writeFile( + "superstatic.json", + '{"firebase": "superstatic", "public": "./"}', + "utf-8", + ); + + const config = loadConfigFile({ public: "app" }, false); + + expect(config).to.eql({ public: "app" }); + + await fs.rm("superstatic.json"); + }); + + it("returns object as-is without merging with firebase.json", async () => { + await fs.writeFile( + "firebase.json", + '{"firebase": "example", "public": "./"}', + "utf-8", + ); + + const config = loadConfigFile({ public: "app" }, false); + + expect(config).to.eql({ public: "app" }); + + await fs.rm("firebase.json"); + }); + + it("returns file config as-is when mergeConfig is false and config is a file path", async () => { + await fs.writeFile( + ".tmp/myconfig.json", + '{"public": "app"}', + "utf-8", + ); + + const config = loadConfigFile(".tmp/myconfig.json", false); + + expect(config).to.eql({ public: "app" }); + }); + }); + describe("extends the file config with the object passed", () => { it("superstatic.json", async () => { await fs.writeFile(