graphile-config
Advanced tools
Comparing version 0.0.1-beta.9 to 0.0.1-beta.10
# graphile-config | ||
## 0.0.1-beta.10 | ||
### Patch Changes | ||
- [#2188](https://github.com/graphile/crystal/pull/2188) | ||
[`cc0941731`](https://github.com/graphile/crystal/commit/cc0941731a1679bc04ce7b7fd4254009bb5f1f62) | ||
Thanks [@benjie](https://github.com/benjie)! - Overhaul the way in which | ||
`graphile-config` presets work such that including a preset at two different | ||
layers shouldn't result in unexpected behavior. | ||
- [#2155](https://github.com/graphile/crystal/pull/2155) | ||
[`8b472cd51`](https://github.com/graphile/crystal/commit/8b472cd51cd66d8227f9f2722d09c0a774792b0f) | ||
Thanks [@benjie](https://github.com/benjie)! - `disablePlugins` now supports | ||
TypeScript auto-completion of known plugin names. Other names are still | ||
accepted without error, so this is just a minor DX improvement rather than | ||
type safety. | ||
- [#2160](https://github.com/graphile/crystal/pull/2160) | ||
[`9cd9bb522`](https://github.com/graphile/crystal/commit/9cd9bb5222a9f0398ee4b8bfa4f741b6de2a2192) | ||
Thanks [@benjie](https://github.com/benjie)! - Add support for lists of hook | ||
objects, so that the same hook can be applied multiple times in the same | ||
plugin but with different priorities. | ||
## 0.0.1-beta.9 | ||
@@ -4,0 +27,0 @@ |
@@ -1,3 +0,3 @@ | ||
import type { CallbackOrDescriptor, FunctionalityObject } from "./interfaces.js"; | ||
export declare function orderedApply<TFunctionality extends FunctionalityObject<TFunctionality>>(plugins: readonly GraphileConfig.Plugin[] | undefined, functionalityRetriever: (plugin: GraphileConfig.Plugin) => Partial<TFunctionality> | undefined, applyCallback: <TFunctionalityName extends keyof TFunctionality>(functionalityName: TFunctionalityName, hookFn: TFunctionality[TFunctionalityName] extends CallbackOrDescriptor<infer U> ? U : never, plugin: GraphileConfig.Plugin) => void): void; | ||
import type { CallbackDescriptor, CallbackOrDescriptor, FunctionalityObject } from "./interfaces.js"; | ||
export declare function orderedApply<TFunctionality extends FunctionalityObject<TFunctionality>>(plugins: readonly GraphileConfig.Plugin[] | undefined, functionalityRetriever: (plugin: GraphileConfig.Plugin) => Partial<TFunctionality> | undefined, applyCallback: <TFunctionalityName extends keyof TFunctionality>(functionalityName: TFunctionalityName, hookFn: TFunctionality[TFunctionalityName] extends CallbackOrDescriptor<infer U> ? U : TFunctionality[TFunctionalityName] extends ReadonlyArray<CallbackDescriptor<infer U>> ? U : never, plugin: GraphileConfig.Plugin) => void): void; | ||
//# sourceMappingURL=functionality.d.ts.map |
@@ -5,2 +5,8 @@ "use strict"; | ||
const sort_js_1 = require("./sort.js"); | ||
// TypeScript nonsense | ||
const isCallbackDescriptor = (v) => typeof v !== "function"; | ||
const isCallback = (v) => typeof v === "function"; | ||
function isArray(arg) { | ||
return Array.isArray(arg); | ||
} | ||
function orderedApply(plugins, functionalityRetriever, applyCallback) { | ||
@@ -18,26 +24,26 @@ // Normalize all the hooks and gather them into collections | ||
for (const key of keys) { | ||
const hookSpecRaw = hooks[key]; | ||
if (!hookSpecRaw) { | ||
const value = hooks[key]; | ||
if (!value) { | ||
continue; | ||
} | ||
// TypeScript nonsense | ||
const isCallbackDescriptor = (v) => typeof v !== "function"; | ||
const isCallback = (v) => typeof v === "function"; | ||
const callback = (isCallback(hookSpecRaw) ? hookSpecRaw : hookSpecRaw.callback); | ||
const { provides, before, after } = isCallbackDescriptor(hookSpecRaw) | ||
? hookSpecRaw | ||
: {}; | ||
if (!allFunctionalities[key]) { | ||
allFunctionalities[key] = []; | ||
const hookList = isArray(value) ? value : [value]; | ||
for (const hookSpecRaw of hookList) { | ||
const callback = (isCallback(hookSpecRaw) ? hookSpecRaw : hookSpecRaw.callback); | ||
const { provides, before, after } = isCallbackDescriptor(hookSpecRaw) | ||
? hookSpecRaw | ||
: {}; | ||
if (!allFunctionalities[key]) { | ||
allFunctionalities[key] = []; | ||
} | ||
// We need to give each functionality a unique ID | ||
const id = String(uid++); | ||
allFunctionalities[key].push({ | ||
id, | ||
plugin, | ||
callback, | ||
provides: [...(provides || []), id, plugin.name], | ||
before: before || [], | ||
after: after || [], | ||
}); | ||
} | ||
// We need to give each functionality a unique ID | ||
const id = String(uid++); | ||
allFunctionalities[key].push({ | ||
id, | ||
plugin, | ||
callback, | ||
provides: [...(provides || []), id, plugin.name], | ||
before: before || [], | ||
after: after || [], | ||
}); | ||
} | ||
@@ -44,0 +50,0 @@ } |
@@ -21,5 +21,5 @@ import { orderedApply } from "./functionality.js"; | ||
/** @deprecated Use CallbackOrDescriptor */ | ||
export type PluginHook<T extends (...args: any[]) => PromiseOrDirect<UnwrapCallback<any> | void>> = CallbackOrDescriptor<T>; | ||
export type PluginHook<T extends (...args: any[]) => PromiseOrDirect<UnwrapCallback<any> | void>> = CallbackOrDescriptor<T> | readonly CallbackDescriptor<T>[]; | ||
/** @deprecated Use UnwrapCallback */ | ||
export type PluginHookCallback<T extends CallbackOrDescriptor<(...args: any[]) => any>> = UnwrapCallback<T>; | ||
//# sourceMappingURL=hooks.d.ts.map |
@@ -12,10 +12,24 @@ import "./interfaces.js"; | ||
namespace GraphileConfig { | ||
/** | ||
* Expand this through declaration merging to get TypeScript | ||
* auto-completion of plugin names in the relevant places. | ||
*/ | ||
interface Plugins { | ||
[key: string & {}]: true; | ||
} | ||
/** | ||
* Expand this through declaration merging to get TypeScript | ||
* auto-completion of things that plugins can provide. | ||
*/ | ||
interface Provides { | ||
[key: string & {}]: true; | ||
} | ||
interface Plugin { | ||
name: string; | ||
name: keyof GraphileConfig.Plugins; | ||
version: string; | ||
experimental?: boolean; | ||
description?: string; | ||
provides?: string[]; | ||
after?: string[]; | ||
before?: string[]; | ||
provides?: (keyof GraphileConfig.Plugins | keyof GraphileConfig.Provides)[]; | ||
after?: (keyof GraphileConfig.Plugins | keyof GraphileConfig.Provides)[]; | ||
before?: (keyof GraphileConfig.Plugins | keyof GraphileConfig.Provides)[]; | ||
} | ||
@@ -30,3 +44,3 @@ /** | ||
plugins?: Plugin[]; | ||
disablePlugins?: ReadonlyArray<string>; | ||
disablePlugins?: ReadonlyArray<keyof GraphileConfig.Plugins>; | ||
appendPlugins?: never; | ||
@@ -39,3 +53,3 @@ prependPlugins?: never; | ||
plugins?: Plugin[]; | ||
disablePlugins?: ReadonlyArray<string>; | ||
disablePlugins?: ReadonlyArray<keyof GraphileConfig.Plugins>; | ||
} | ||
@@ -42,0 +56,0 @@ } |
export type AnyCallback = (...args: any[]) => any; | ||
export type CallbackDescriptor<T extends AnyCallback> = { | ||
provides?: string[]; | ||
before?: string[]; | ||
after?: string[]; | ||
provides?: (keyof GraphileConfig.Plugins | keyof GraphileConfig.Provides)[]; | ||
before?: (keyof GraphileConfig.Plugins | keyof GraphileConfig.Provides)[]; | ||
after?: (keyof GraphileConfig.Plugins | keyof GraphileConfig.Provides)[]; | ||
callback: T; | ||
@@ -10,4 +10,4 @@ }; | ||
export type CallbackOrDescriptor<T extends AnyCallback> = T | CallbackDescriptor<T>; | ||
export type UnwrapCallback<T extends CallbackOrDescriptor<AnyCallback>> = T extends CallbackOrDescriptor<infer U> ? U : never; | ||
export type FunctionalityObject<T> = Record<keyof T, CallbackOrDescriptor<AnyCallback>>; | ||
export type UnwrapCallback<T extends CallbackOrDescriptor<AnyCallback> | ReadonlyArray<CallbackDescriptor<AnyCallback>>> = T extends CallbackOrDescriptor<infer U> ? U : T extends ReadonlyArray<CallbackDescriptor<infer U>> ? U : never; | ||
export type FunctionalityObject<T> = Record<keyof T, CallbackOrDescriptor<AnyCallback> | ReadonlyArray<CallbackDescriptor<AnyCallback>>>; | ||
//# sourceMappingURL=interfaces.d.ts.map |
@@ -7,3 +7,3 @@ import "./interfaces.js"; | ||
*/ | ||
export declare function resolvePresets(presets: ReadonlyArray<GraphileConfig.Preset>, withAssertions?: boolean): GraphileConfig.ResolvedPreset; | ||
export declare function resolvePresets(presets: ReadonlyArray<GraphileConfig.Preset>): GraphileConfig.ResolvedPreset; | ||
//# sourceMappingURL=resolvePresets.d.ts.map |
@@ -40,3 +40,8 @@ "use strict"; | ||
function isResolvedPreset(preset) { | ||
return (preset.plugins && preset.extends?.length === 0) || false; | ||
return ((preset.plugins && | ||
preset.extends?.length === 0 && | ||
(!preset.disablePlugins || | ||
!preset.plugins || | ||
!preset.plugins.some((p) => preset.disablePlugins.includes(p.name)))) || | ||
false); | ||
} | ||
@@ -48,3 +53,3 @@ exports.isResolvedPreset = isResolvedPreset; | ||
*/ | ||
function resolvePresets(presets, withAssertions = true) { | ||
function resolvePresets(presets) { | ||
if (presets.length === 1) { | ||
@@ -57,6 +62,23 @@ // Maybe it's already resolved? | ||
} | ||
const seenPluginNames = new Set(); | ||
const resolvedPreset = resolvePresetsInternal(presets, seenPluginNames, 0); | ||
const disabledButNotSeen = resolvedPreset.disablePlugins?.filter((n) => !seenPluginNames.has(n)); | ||
if (disabledButNotSeen?.length) { | ||
console.warn(`One or more of the plugin(s) entered in your preset's 'disablePlugins' list was never seen - perhaps you have misspelled them?\n${disabledButNotSeen | ||
.map((p) => ` - ${p}`) | ||
.join("\n")}\nThe list of know plugins is:\n ${[...seenPluginNames].join(", ") ?? "-"}`); | ||
} | ||
return resolvedPreset; | ||
} | ||
exports.resolvePresets = resolvePresets; | ||
function resolvePresetsInternal(presets, seenPluginNames, depth) { | ||
const finalPreset = blankResolvedPreset(); | ||
for (const preset of presets) { | ||
const resolvedPreset = resolvePreset(preset); | ||
mergePreset(finalPreset, resolvedPreset); | ||
if (isResolvedPreset(preset) && preset.disablePlugins) { | ||
for (const p of preset.disablePlugins) { | ||
seenPluginNames.add(p); | ||
} | ||
} | ||
const resolvedPreset = resolvePresetInternal(preset, seenPluginNames, depth + 1); | ||
mergePreset(finalPreset, resolvedPreset, seenPluginNames, depth); | ||
} | ||
@@ -66,12 +88,4 @@ if (finalPreset.plugins) { | ||
} | ||
if (withAssertions) { | ||
if (finalPreset.disablePlugins && finalPreset.disablePlugins.length > 0) { | ||
console.warn(`One or more of the plugin(s) entered in your preset's 'disablePlugins' list was not found:\n${finalPreset.disablePlugins | ||
.map((p) => ` - ${p}`) | ||
.join("\n")}\nThe list of know plugins is:\n ${finalPreset.plugins?.map((p) => p.name).join(", ") ?? "-"}`); | ||
} | ||
} | ||
return finalPreset; | ||
} | ||
exports.resolvePresets = resolvePresets; | ||
function isGraphileConfigPreset(foo) { | ||
@@ -132,3 +146,3 @@ if (typeof foo !== "object" || foo === null) | ||
*/ | ||
function resolvePreset(preset) { | ||
function resolvePresetInternal(preset, seenPluginNames, depth) { | ||
if (!isGraphileConfigPreset(preset)) { | ||
@@ -153,23 +167,14 @@ throw new Error(`Expected a GraphileConfig preset (a plain JS object), but found '${inspect(preset)}'`); | ||
} | ||
const { extends: presets = [] } = preset; | ||
const basePreset = resolvePresets(presets, false); | ||
mergePreset(basePreset, preset); | ||
const disabled = basePreset.disablePlugins; | ||
if (disabled) { | ||
const plugins = new Set(basePreset.plugins); | ||
const remaining = new Set(disabled); | ||
for (const plugin of plugins) { | ||
assertPlugin(plugin); | ||
if (remaining.has(plugin.name)) { | ||
remaining.delete(plugin.name); | ||
plugins.delete(plugin); | ||
} | ||
} | ||
basePreset.plugins = [...plugins]; | ||
basePreset.disablePlugins = [...remaining]; | ||
} | ||
} | ||
catch (e) { | ||
throw new Error(`Error occurred when resolving preset:\n ${String(e).replace(/\n/g, "\n ")}\nPreset: ${inspect(preset)}`); | ||
} | ||
const { extends: presets = [], ...rest } = preset; | ||
const basePreset = resolvePresetsInternal(presets, seenPluginNames, depth + 1); | ||
try { | ||
mergePreset(basePreset, rest, seenPluginNames, depth); | ||
return basePreset; | ||
} | ||
catch (e) { | ||
throw new Error(`Error occurred when resolving preset: ${e}\nPreset: ${inspect(preset)}`); | ||
throw new Error(`Error occurred when resolving preset:\n ${String(e).replace(/\n/g, "\n ")}\nPreset: ${inspect(preset)}`); | ||
} | ||
@@ -185,6 +190,29 @@ } | ||
*/ | ||
function mergePreset(targetPreset, sourcePreset) { | ||
function mergePreset(targetPreset, sourcePreset, seenPluginNames, _depth) { | ||
const sourcePluginNames = []; | ||
if (sourcePreset.plugins) { | ||
for (const plugin of sourcePreset.plugins) { | ||
assertPlugin(plugin); | ||
seenPluginNames.add(plugin.name); | ||
sourcePluginNames.push(plugin.name); | ||
} | ||
} | ||
if (targetPreset.extends != null && targetPreset.extends.length !== 0) { | ||
throw new Error("First argument to mergePreset must be a resolved preset"); | ||
} | ||
const addedAndDisabled = sourcePreset.disablePlugins | ||
? sourcePluginNames.filter((addedPluginName) => sourcePreset.disablePlugins.includes(addedPluginName)) | ||
: []; | ||
if (addedAndDisabled.length > 0) { | ||
throw new Error(`A preset may not both add a plugin and disable that same plugin ('${addedAndDisabled.join("', '")}')`); | ||
} | ||
const disablePlugins = [ | ||
...new Set([ | ||
// Remove the previously disabled plugins where we've explicitly re-added the plugin | ||
...(targetPreset.disablePlugins?.filter((pluginName) => !sourcePluginNames.includes(pluginName)) ?? []), | ||
// Explicitly add our new disablePlugins | ||
...(sourcePreset.disablePlugins ?? []), | ||
]), | ||
]; | ||
targetPreset.disablePlugins = disablePlugins; | ||
const plugins = new Set([ | ||
@@ -194,11 +222,4 @@ ...(targetPreset.plugins || []), | ||
]); | ||
targetPreset.plugins = [...plugins]; | ||
if (sourcePreset.disablePlugins) { | ||
targetPreset.disablePlugins = [ | ||
...new Set([ | ||
...(targetPreset.disablePlugins ?? []), | ||
...(sourcePreset.disablePlugins ?? []), | ||
]), | ||
]; | ||
} | ||
// Copy the unique plugins that are not disabled | ||
targetPreset.plugins = [...plugins].filter((p) => !disablePlugins.includes(p.name)); | ||
const targetScopes = Object.keys(targetPreset).filter(isScopeKeyForPreset); | ||
@@ -205,0 +226,0 @@ const sourceScopes = Object.keys(sourcePreset).filter(isScopeKeyForPreset); |
{ | ||
"name": "graphile-config", | ||
"version": "0.0.1-beta.9", | ||
"version": "0.0.1-beta.10", | ||
"description": "Standard plugin interface and helpers to be used across the Graphile stack.", | ||
@@ -5,0 +5,0 @@ "type": "commonjs", |
@@ -129,7 +129,6 @@ # graphile-config | ||
<td align="center"><a href="https://dovetailapp.com/"><img src="https://graphile.org/images/sponsors/dovetail.png" width="90" height="90" alt="Dovetail" /><br />Dovetail</a> *</td> | ||
<td align="center"><a href="https://www.netflix.com/"><img src="https://graphile.org/images/sponsors/Netflix.png" width="90" height="90" alt="Netflix" /><br />Netflix</a> *</td> | ||
<td align="center"><a href="https://stellate.co/"><img src="https://graphile.org/images/sponsors/Stellate.png" width="90" height="90" alt="Stellate" /><br />Stellate</a> *</td> | ||
<td align="center"><a href="https://gosteelhead.com/"><img src="https://graphile.org/images/sponsors/steelhead.svg" width="90" height="90" alt="Steelhead" /><br />Steelhead</a> *</td> | ||
</tr><tr> | ||
<td align="center"><a href="https://gosteelhead.com/"><img src="https://graphile.org/images/sponsors/steelhead.svg" width="90" height="90" alt="Steelhead" /><br />Steelhead</a> *</td> | ||
<td align="center"><a href="https://www.sylvera.com/"><img src="https://graphile.org/images/sponsors/sylvera.svg" width="90" height="90" alt="Sylvera" /><br />Sylvera</a> *</td> | ||
<td align="center"><a href=""><img src="https://graphile.org/images/sponsors/latchbio.jpg" width="90" height="90" alt="LatchBio" /><br />LatchBio</a> *</td> | ||
</tr></table> | ||
@@ -136,0 +135,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
88467
827
166