Skip to content

Commit

Permalink
Merge pull request #3629 from GMOD/configuration-typescript
Browse files Browse the repository at this point in the history
typescript checking for config slot names
  • Loading branch information
rbuels authored Apr 19, 2023
2 parents ebb9cdf + 9ee6eb9 commit 49b01fd
Show file tree
Hide file tree
Showing 46 changed files with 282 additions and 115 deletions.
2 changes: 1 addition & 1 deletion packages/core/Plugin.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import PluginManager from './PluginManager'
import { AnyConfigurationSchemaType } from './configuration/configurationSchema'
import { AnyConfigurationSchemaType } from './configuration'

/**
* base class for a JBrowse plugin
Expand Down
6 changes: 3 additions & 3 deletions packages/core/assemblyManager/assembly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,9 +381,9 @@ export default function assemblyFactory(
*/
async loadPre() {
const conf = self.configuration
const refNameAliasesAdapterConf = conf.refNameAliases?.adapter
const cytobandAdapterConf = conf.cytobands?.adapter
const sequenceAdapterConf = conf.sequence.adapter
const refNameAliasesAdapterConf = conf?.refNameAliases?.adapter
const cytobandAdapterConf = conf?.cytobands?.adapter
const sequenceAdapterConf = conf?.sequence.adapter
const assemblyName = self.name

const regions = await getAssemblyRegions(sequenceAdapterConf, pm)
Expand Down
50 changes: 50 additions & 0 deletions packages/core/configuration/configurationSchema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { types, getSnapshot } from 'mobx-state-tree'
import { ConfigurationSchema } from './configurationSchema'
import { isConfigurationModel } from './util'
import { getConf, readConfObject } from '.'
// import { ConfigurationSchemaForModel, GetOptions, GetBase, ConfigurationSlotName } from './types'

describe('configuration schemas', () => {
test('can make a schema with a color', () => {
Expand All @@ -21,6 +22,7 @@ describe('configuration schemas', () => {
})

const model = container.create()

expect(isConfigurationModel(model.configuration)).toBe(true)
expect(getConf(model, 'backgroundColor')).toBe('#eee')
expect(getConf(model, 'someInteger')).toBe(12)
Expand All @@ -35,6 +37,13 @@ describe('configuration schemas', () => {
expect(getConf(model, 'someInteger', { a: 5 })).toBe(10)
model.configuration.someInteger.set(42)
expect(getConf(model, 'someInteger', { a: 5 })).toBe(42)

// typescript tests
// const conf = model.configuration
// let schema: ConfigurationSchemaForModel<typeof conf>
// let options: GetOptions<typeof schema>
// let base: GetBase<typeof schema>
// let slot: ConfigurationSlotName<typeof schema>
})

test('can nest an array of configuration schemas', () => {
Expand Down Expand Up @@ -86,6 +95,47 @@ describe('configuration schemas', () => {
// expect(getConf(model, 'mySubConfiguration.someNumber')).toBe(4.3)
})

test('a schema can inherit from another base schema', () => {
const base = ConfigurationSchema('Foo', {
someInteger: {
description: 'an integer slot',
type: 'integer',
defaultValue: 12,
},
mySubConfiguration: ConfigurationSchema('SubObject', {
someNumber: {
description: 'some number in a subconfiguration',
type: 'number',
defaultValue: 4.3,
},
}),
})

const child = ConfigurationSchema(
'Bar',
{
anotherInteger: {
type: 'integer',
defaultValue: 4,
},
},
{
baseConfiguration: base,
},
)

const model = child.create()
expect(isConfigurationModel(model)).toBe(true)
expect(readConfObject(model, 'someInteger')).toBe(12)

// typescript tests
// const conf = model
// let schema: ConfigurationSchemaForModel<typeof conf>
// let options: GetOptions<typeof schema>
// let baseConf: GetBase<typeof schema>
// let slot: ConfigurationSlotName<typeof schema>
})

test('can snapshot a simple schema', () => {
const container = types.model({
configuration: ConfigurationSchema('Foo', {
Expand Down
66 changes: 39 additions & 27 deletions packages/core/configuration/configurationSchema.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
types,
isStateTreeNode,
isType,
isLateType,
getSnapshot,
Instance,
IAnyType,
SnapshotOut,
} from 'mobx-state-tree'
Expand All @@ -13,6 +13,14 @@ import { ElementId } from '../util/types/mst'

import ConfigSlot, { ConfigSlotDefinition } from './configurationSlot'
import { isConfigurationSchemaType } from './util'
import { AnyConfigurationSchemaType } from './types'

export type {
AnyConfigurationSchemaType,
AnyConfigurationModel,
AnyConfigurationSlot,
AnyConfigurationSlotType,
} from './types'

function isEmptyObject(thing: unknown) {
return (
Expand All @@ -36,22 +44,25 @@ export interface ConfigurationSchemaDefinition {
| IAnyType
}

interface ConfigurationSchemaOptions {
export interface ConfigurationSchemaOptions<
BASE_SCHEMA extends AnyConfigurationSchemaType | undefined,
EXPLICIT_IDENTIFIER extends string | undefined,
> {
explicitlyTyped?: boolean
explicitIdentifier?: string
explicitIdentifier?: EXPLICIT_IDENTIFIER
implicitIdentifier?: string | boolean
baseConfiguration?: AnyConfigurationSchemaType
baseConfiguration?: BASE_SCHEMA

actions?: (self: unknown) => any // eslint-disable-line @typescript-eslint/no-explicit-any
views?: (self: unknown) => any // eslint-disable-line @typescript-eslint/no-explicit-any
extend?: (self: unknown) => any // eslint-disable-line @typescript-eslint/no-explicit-any
actions?: (self: unknown) => any
views?: (self: unknown) => any
extend?: (self: unknown) => any
preProcessSnapshot?: (snapshot: {}) => {}
}

function preprocessConfigurationSchemaArguments(
modelName: string,
inputSchemaDefinition: ConfigurationSchemaDefinition,
inputOptions: ConfigurationSchemaOptions = {},
inputOptions: ConfigurationSchemaOptions<any, any> = {},
) {
if (typeof modelName !== 'string') {
throw new Error(
Expand Down Expand Up @@ -82,10 +93,9 @@ function preprocessConfigurationSchemaArguments(

function makeConfigurationSchemaModel<
DEFINITION extends ConfigurationSchemaDefinition,
OPTIONS extends ConfigurationSchemaOptions,
OPTIONS extends ConfigurationSchemaOptions<any, any>,
>(modelName: string, schemaDefinition: DEFINITION, options: OPTIONS) {
// now assemble the MST model of the configuration schema
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const modelDefinition: Record<string, any> = {}
let identifier

Expand Down Expand Up @@ -116,7 +126,6 @@ function makeConfigurationSchemaModel<
}
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const volatileConstants: Record<string, any> = {
isJBrowseConfigurationSchema: true,
jbrowseSchema: {
Expand Down Expand Up @@ -231,29 +240,27 @@ function makeConfigurationSchemaModel<
return types.optional(completeModel, modelDefault)
}

export interface AnyConfigurationSchemaType
extends ReturnType<typeof makeConfigurationSchemaModel> {
export interface ConfigurationSchemaType<
DEFINITION extends ConfigurationSchemaDefinition,
OPTIONS extends ConfigurationSchemaOptions<any, any>,
> extends ReturnType<typeof makeConfigurationSchemaModel<DEFINITION, OPTIONS>> {
isJBrowseConfigurationSchema: boolean
jbrowseSchemaDefinition: ConfigurationSchemaDefinition
jbrowseSchemaOptions: ConfigurationSchemaOptions
jbrowseSchemaDefinition: DEFINITION
jbrowseSchemaOptions: OPTIONS
type: string
[key: string]: unknown
}

export type AnyConfigurationModel = Instance<AnyConfigurationSchemaType>
export type AnyConfigurationSlotType = ReturnType<typeof ConfigSlot>
export type AnyConfigurationSlot = Instance<AnyConfigurationSlotType>

export type ConfigurationModel<SCHEMA extends AnyConfigurationSchemaType> =
Instance<SCHEMA>

export function ConfigurationSchema<
DEFINITION extends ConfigurationSchemaDefinition,
OPTIONS extends ConfigurationSchemaOptions,
OPTIONS extends ConfigurationSchemaOptions<BASE_SCHEMA, EXPLICIT_IDENTIFIER>,
BASE_SCHEMA extends AnyConfigurationSchemaType | undefined = undefined,
EXPLICIT_IDENTIFIER extends string | undefined = undefined,
>(
modelName: string,
inputSchemaDefinition: DEFINITION,
inputOptions?: OPTIONS,
) {
inputOptions?: ConfigurationSchemaOptions<BASE_SCHEMA, EXPLICIT_IDENTIFIER>,
): ConfigurationSchemaType<DEFINITION, OPTIONS> {
const { schemaDefinition, options } = preprocessConfigurationSchemaArguments(
modelName,
inputSchemaDefinition,
Expand All @@ -271,6 +278,11 @@ export function ConfigurationSchema<
return schemaType
}

export function ConfigurationReference(schemaType: IAnyType) {
return types.union(types.reference(schemaType), schemaType)
export function ConfigurationReference<
SCHEMATYPE extends AnyConfigurationSchemaType,
>(schemaType: SCHEMATYPE) {
// we cast this to SCHEMATYPE, because the reference *should* behave just
// like the object it points to. It won't be undefined (this is a
// `reference`, not a `safeReference`)
return types.union(types.reference(schemaType), schemaType) as SCHEMATYPE
}
6 changes: 4 additions & 2 deletions packages/core/configuration/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ export {
} from './configurationSchema'

export type {
AnyConfigurationModel,
AnyConfigurationSchemaType,
} from './configurationSchema'
AnyConfigurationModel,
AnyConfigurationSlot,
AnyConfigurationSlotType,
} from './types'

export * from './util'
65 changes: 65 additions & 0 deletions packages/core/configuration/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { IStateTreeNode, Instance } from 'mobx-state-tree'
import type {
ConfigurationSchemaType,
ConfigurationSchemaOptions,
} from './configurationSchema'
import type ConfigSlot from './configurationSlot'

export type GetOptions<SCHEMA> = SCHEMA extends ConfigurationSchemaType<
any,
infer OPTIONS
>
? OPTIONS
: never

// type GetDefinition<SCHEMA> = SCHEMA extends ConfigurationSchemaType<
// infer D,
// any
// >
// ? D
// : never

export type GetBase<SCHEMA> = SCHEMA extends undefined
? never
: GetOptions<SCHEMA> extends ConfigurationSchemaOptions<undefined, any>
? undefined
: GetOptions<SCHEMA> extends ConfigurationSchemaOptions<
infer BASE extends AnyConfigurationSchemaType,
any
>
? BASE
: never

export type GetExplicitIdentifier<SCHEMA> =
GetOptions<SCHEMA> extends ConfigurationSchemaOptions<
any,
infer EXPLICIT_IDENTIFIER extends string
>
? EXPLICIT_IDENTIFIER
: never

export type ConfigurationSchemaForModel<MODEL> = MODEL extends IStateTreeNode<
infer SCHEMA extends AnyConfigurationSchemaType
>
? SCHEMA
: never

export type ConfigurationSlotName<SCHEMA> = SCHEMA extends undefined
? never
: SCHEMA extends ConfigurationSchemaType<infer D, any>
?
| (keyof D & string)
| GetExplicitIdentifier<SCHEMA>
| (GetBase<SCHEMA> extends ConfigurationSchemaType<any, any>
? ConfigurationSlotName<GetBase<SCHEMA>>
: never)
: never

export type AnyConfigurationSchemaType = ConfigurationSchemaType<any, any>
export type AnyConfigurationModel = Instance<AnyConfigurationSchemaType>
export type AnyConfigurationSlotType = ReturnType<typeof ConfigSlot>
export type AnyConfigurationSlot = Instance<AnyConfigurationSlotType>

export type ConfigurationModel<SCHEMA extends AnyConfigurationSchemaType> =
Instance<SCHEMA>
Loading

0 comments on commit 49b01fd

Please sign in to comment.