PRERELEASE: this is pre-release software; use at your own risk. This will likely change a lot before it's ultimately released.
graphile-config
provides a standard plugin interface and helpers that can be
used across the entire of the Graphile suite. Primarily users will only use this
as import type Plugin from 'graphile-config';
so that they can export plugins.
This package provides two interfaces: Plugin
and Preset
(alias Config
).
A plugin is responsible for adding capabilities to a Graphile package. Each Graphile package will register its own "scope" within the plugin's spec; commonly these scopes may contain capabilities such as 'hooks' or 'events' which this package attempts to standardize.
A Graphile Plugin is an object with the following properties:
name
(string
): The name of the plugin, this must be unique and will be used for capabilities such asskipPlugins
version
(string
): a semver-compliant version for the plugin, this would normally match the version in thepackage.json
but does not need to (e.g. if the module in question contains multiple plugins)description
(optionalstring
): human-readable description of the plugin in CommonMark (markdown) format.provides
(optionalstring[]
): an optional list of "feature labels" that this plugin provides, this is primarily used to govern the order in which the plugin (and its hooks and events) are executed. Feature labels must be unique within the list of loaded plugins, for example two different plugins should not both providesubscriptions
. If unspecified, defaults to the plugin name.after
(optionalstring[]
): indicates that this plugin should be loaded after the named features (if present)before
(optionalstring[]
): indicates that this plugin should be loaded before the named features (if present)
In addition to the properties above, plugins may also contain properties for
each of the supported scopes, for example there may be a postgraphile
scope
for PostGraphile, or a worker
scope for Graphile Worker. The value for each of
these scopes will be an object, but the contents of that object are defined by
the projects in question.
NOTE: Currently this plugin system is only intended for Graphile usage (and thus we do not need to "reserve" keys), but should you find it useful for other projects please reach out via GitHub issues and we can discuss what's necessary to make this more universal. Should you decide to not heed this advice, please at least make sure that the "scopes" you add are namespaced in a way to avoid future conflicts with features we may wish to add.
A preset bundles together a list of plugins, and options for various of the
"scopes". You may use more than one preset at a time, and presets may also
compose (extend) other presets. When a library is passed a list of presets it
results in a resolved preset (a preset that has no "extends") using the
ResolvePresets
algorithm; broadly all the extends
are resolved in order, the
plugins specified are merged as a set (each plugin will only be included once)
and the options are merged via object merging such that the options specified
last win.
NOTE: if you compose two presets (PresetA and PresetB) that both extends
the same underlying preset (BASE) and apply some overrides, then the overrides
in PresetA will be overridden by re-applying the BASE preset again. For this
reason, presets that are expected to be combined with other presets should not
extends
common/shared presets, instead the end-user should be expected to add
these presets themselves.
NOTE: the order of presets is significant.
ResolvePresets(presets):
- Let {finalPreset} be an empty preset.
- For each {preset} in {presets}:
- Let {resolvedPreset} be {ResolvePreset(preset)}.
- Let {finalPreset} be {MergePreset(finalPreset, resolvedPreset)}.
- Return {finalPreset}.
ResolvePreset(preset):
- Let {presets} be the list specified in the {extends} property of {preset} (or an empty list if none specified).
- Let {basePreset} be {ResolvePresets(presets)}.
- Return {MergePreset(basePreset, preset)}.
MergePreset(basePreset, extendingPreset):
- Let {finalPreset} be an empty preset.
- Assert: {basePreset} has an empty or non-existent {extends} property.
- Let {plugins} be the list of plugins defined in {basePreset} union the list of plugins in {extendingPreset}.
- Let the list of plugins for {finalPreset} be {plugins}.
- Let {scopes} be the list of scopes defined in {basePreset} union the list of scopes in {extendingPreset}.
- For each {scope} in {scopes}:
- Let {baseScope} be the {scope} in {basePreset}.
- Let {extendingScope} be the {scope} in {extendingPreset}.
- If {baseScope} and {extendingScope} both exist:
- Let {scope} in {finalPreset} be the result of merging {baseScope} and
{extendingScope} akin to
Object.assign({}, baseScope, extendingScope)
.
- Let {scope} in {finalPreset} be the result of merging {baseScope} and
{extendingScope} akin to
- Else: let {scope} in {finalPreset} be whichever of {baseScope} and {extendingScope} actually exist.
- Return {finalPreset}.
IMPORTANT: the default
name must not be used as a top-level key in a
preset to enable compatibility with the various ESM emulations.
To help us develop this software sustainably, we ask all individuals and businesses that use it to help support its ongoing maintenance and development via sponsorship.
And please give some love to our featured sponsors 🤩:
The Guild * |
Dovetail * |
Stellate * |
Steelhead * |
LatchBio * |
* Sponsors the entire Graphile suite
You can specify a graphile.config.ts
file; but if that uses export default
and your TypeScript is configured to export ESM then you'll get an error telling
you that you cannot require
an ES Module:
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /path/to/graphile.config.ts
require() of ES modules is not supported.
require() of /path/to/graphile.config.ts from /path/to/node_modules/graphile-config/dist/loadConfig.js is an ES module file as it is a .ts file whose nearest parent package.json contains "type": "module" which defines all .ts files in that package scope as ES modules.
Instead change the requiring code to use import(), or remove "type": "module" from /path/to/package.json.
Or, in newer versions, an error saying unknown file extension:
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /path/to/graphile.config.ts
To solve this, use the experimental loaders API to add support for TS ESM via
the ts-node/esm
loader:
export NODE_OPTIONS="$NODE_OPTIONS --loader ts-node/esm"
Then run your command again.