cli
cli copied to clipboard
Create global configuration store
Reading the Netlify configuration file is a very common problem throughout the CLI codebase. In some cases, it's sufficient to read the initial value of the config (i.e. the state of the config when the application starts), but in other cases it's important to watch for any updates and update the application accordingly.
We currently do this in a few places in a non-consistent way. For example, we have this watcher, using a callback pattern, that is looking for updates to headers and redirects. When netlify.toml
is updated, this is printed in the console (even if there were no actual changes to headers or redirects):
◈ Reloading headers files from [ 'netlify.toml' ]
◈ Reloading redirect rules from [ 'netlify.toml' ]
Separately, the Dev command has its own config watcher, using an event-driven pattern. This is tightly coupled with the Graph functionality though, making it difficult to reuse it for more generic applications.
I propose that we create a consistent way of solving this problem by standing up a global configuration store. This store would expose an API with the following characteristics:
Returning a config value using a declarative way
The store would keep a cached version of the config, parsed and normalised, which it would update automatically when it detects a change in the configuration file.
const { config } = command.netlify
const buildCommand = await config.get("build.command")
However, certain configuration values can be set remotely (e.g. in the UI), and the config store wouldn't be able to easily receive updates of those changes.
So the .get()
command accepts an optional parameter that should be used when the consumer requires a fresh value that may come from a remote API, forcing the config store to fetch it as opposed to using the cached version.
const { config } = command.netlify
const buildCommand = await config.get("build.command", { refresh: true })
Listening for changes using events
The store emits a change
even every time it detects a change in the local configuration file, and it allows consumers to subscribe to them.
const { config } = command.netlify
config.on('change', () => {
const newBuildCommand = await config.get("build.command")
})
To make user messaging more useful, the config store should compute the diff between the old config and the new config (using something like https://www.npmjs.com/package/deep-object-diff) and expose that as a parameter in the change
event. With this, consumers can adjust their messaging according to what actually changed.
For example, the headers and redirects message above could be tweaked so that it's only printed if headers or redirects have changed.
const { config } = command.netlify
config.on('change', (diff) => {
if (diff.headers) {
console.log('◈ Reloaded headers from netlify.toml')
}
if (diff.redirects) {
console.log('◈ Reloaded redirects from netlify.toml')
}
})