From 1502bc5d28868f22ec490e418769f928901ebded Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Mon, 12 Sep 2022 16:02:32 +0200 Subject: [PATCH 1/2] feat!: allow async `$resolve` --- README.md | 6 +++--- src/schema.ts | 16 ++++++++-------- src/types.ts | 4 ++-- test/defaults.test.ts | 4 ++-- test/schema.test.ts | 36 ++++++++++++++++++------------------ test/types.test.ts | 24 ++++++++++++------------ web/app.vue | 4 ++-- 7 files changed, 47 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index fb78bfd..3845fcf 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ const defaultPlanet = { ```js import { resolveSchema } from 'untyped' -const schema = resolveSchema(defaultPlanet) +const schema = await resolveSchema(defaultPlanet) ``` Output: @@ -92,7 +92,7 @@ Output: ```js import { resolveSchema, generateTypes } from 'untyped' -const types = generateTypes(resolveSchema(defaultPlanet)) +const types = generateTypes(await resolveSchema(defaultPlanet)) ``` Output: @@ -120,7 +120,7 @@ interface Untyped { ```js import { resolveSchema, generateMarkdown } from 'untyped' -const markdown = generateMarkdown(resolveSchema(defaultPlanet)) +const markdown = generateMarkdown(await resolveSchema(defaultPlanet)) ``` Output: diff --git a/src/schema.ts b/src/schema.ts index 03abf96..b67092e 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -7,8 +7,8 @@ interface _ResolveCtx { resolveCache: Record } -export function resolveSchema (obj: InputObject, defaults?: InputObject) { - const schema = _resolveSchema(obj, '', { +export async function resolveSchema (obj: InputObject, defaults?: InputObject) { + const schema = await _resolveSchema(obj, '', { root: obj, defaults, resolveCache: {} @@ -18,7 +18,7 @@ export function resolveSchema (obj: InputObject, defaults?: InputObject) { return schema } -function _resolveSchema (input: InputValue, id: string, ctx: _ResolveCtx): Schema { +async function _resolveSchema (input: InputValue, id: string, ctx: _ResolveCtx): Promise { // Check cache if (id in ctx.resolveCache) { return ctx.resolveCache[id] @@ -55,7 +55,7 @@ function _resolveSchema (input: InputValue, id: string, ctx: _ResolveCtx): Schem } schema.properties = schema.properties || {} if (!schema.properties[key]) { - schema.properties[key] = _resolveSchema(node[key], joinPath(id, key), ctx) + schema.properties[key] = await _resolveSchema(node[key], joinPath(id, key), ctx) } } @@ -67,8 +67,8 @@ function _resolveSchema (input: InputValue, id: string, ctx: _ResolveCtx): Schem schema.default = node.$default } if (typeof node.$resolve === 'function') { - schema.default = node.$resolve(schema.default, (key) => { - return _resolveSchema(getValue(ctx.root, key), key, ctx).default + schema.default = await node.$resolve(schema.default, async (key) => { + return (await _resolveSchema(getValue(ctx.root, key), key, ctx)).default }) } if (ctx.defaults) { @@ -87,8 +87,8 @@ function _resolveSchema (input: InputValue, id: string, ctx: _ResolveCtx): Schem return schema } -export function applyDefaults (ref: InputObject, input: InputObject) { - resolveSchema(ref, input) +export async function applyDefaults (ref: InputObject, input: InputObject) { + await resolveSchema(ref, input) return input } diff --git a/src/types.ts b/src/types.ts index e9fb186..42aee17 100644 --- a/src/types.ts +++ b/src/types.ts @@ -22,7 +22,7 @@ export type JSType = 'array' // eslint-disable-next-line no-use-before-define -export type ResolveFn = ((value: any, get: (key: string) => any) => JSValue) +export type ResolveFn = ((value: any, get: (key: string) => any) => JSValue | Promise) export interface TypeDescriptor { /** Used internally to handle schema types */ @@ -55,7 +55,7 @@ export interface Schema extends TypeDescriptor { export interface InputObject { $schema?: Schema - $resolve?: ResolveFn + $resolve?: ResolveFn | Promise $default?: any [key: string]: any } diff --git a/test/defaults.test.ts b/test/defaults.test.ts index 920fee9..dadc8b0 100644 --- a/test/defaults.test.ts +++ b/test/defaults.test.ts @@ -2,7 +2,7 @@ import { describe, it, expect } from 'vitest' import { applyDefaults } from '../src' describe('applyDefaults', () => { - it('basic', () => { + it('basic', async () => { const ref = { name: 'default', empty: {}, @@ -20,7 +20,7 @@ describe('applyDefaults', () => { } } - const applied = applyDefaults(ref, input) + const applied = await applyDefaults(ref, input) expect(applied).toMatchObject({ name: 'custom', diff --git a/test/schema.test.ts b/test/schema.test.ts index 4b82982..8653feb 100644 --- a/test/schema.test.ts +++ b/test/schema.test.ts @@ -2,8 +2,8 @@ import { describe, it, expect } from 'vitest' import { resolveSchema } from '../src' describe('resolveSchema', () => { - it('direct value', () => { - const schema = resolveSchema({ + it('direct value', async () => { + const schema = await resolveSchema({ foo: 'bar', empty: {} }) @@ -22,8 +22,8 @@ describe('resolveSchema', () => { }) }) - it('nested value', () => { - const schema = resolveSchema({ + it('nested value', async () => { + const schema = await resolveSchema({ foo: { bar: 123 } }) expect(schema).toMatchObject({ @@ -41,8 +41,8 @@ describe('resolveSchema', () => { }) }) - it('with $default', () => { - const schema = resolveSchema({ + it('with $default', async () => { + const schema = await resolveSchema({ foo: { $default: 'bar' } }) expect(schema).toMatchObject({ @@ -55,8 +55,8 @@ describe('resolveSchema', () => { }) }) - it('with $schema', () => { - const schema = resolveSchema({ + it('with $schema', async () => { + const schema = await resolveSchema({ foo: { $schema: { title: 'this is foo' } } }) expect(schema).toMatchObject({ @@ -68,8 +68,8 @@ describe('resolveSchema', () => { }) }) - it('with $resolve', () => { - const schema = resolveSchema({ + it('with $resolve', async () => { + const schema = await resolveSchema({ foo: { $default: '123', $resolve: val => parseInt(val) } }) expect(schema).toMatchObject({ @@ -82,10 +82,10 @@ describe('resolveSchema', () => { }) }) - it('with $resolve (dependency order-1)', () => { - const schema = resolveSchema({ + it('with $resolve (dependency order-1)', async () => { + const schema = await resolveSchema({ foo: { $resolve: () => 'foo' }, - bar: { $resolve: (val, get) => get('foo') + (val || 'bar') } + bar: { $resolve: async (val, get) => await get('foo') + (val || 'bar') } }) expect(schema).toMatchObject({ properties: { @@ -96,10 +96,10 @@ describe('resolveSchema', () => { }) }) - it('with $resolve (dependency order-2)', () => { - const schema = resolveSchema({ + it('with $resolve (dependency order-2)', async () => { + const schema = await resolveSchema({ nested: { - foo: { $resolve: (val, get) => get('rootDir') + (val || 'bar') } + foo: { $resolve: async (val, get) => await get('rootDir') + (val || 'bar') } }, rootDir: { $resolve: () => 'root/' } }) @@ -121,8 +121,8 @@ describe('resolveSchema', () => { }) }) - it('array', () => { - const schema = resolveSchema({ + it('array', async () => { + const schema = await resolveSchema({ empty: [], numbers: [1, 2, 3], mixed: [true, 123], diff --git a/test/types.test.ts b/test/types.test.ts index 943f48b..a8a99f4 100644 --- a/test/types.test.ts +++ b/test/types.test.ts @@ -2,8 +2,8 @@ import { describe, it, expect } from 'vitest' import { resolveSchema, generateTypes } from '../src' describe('resolveSchema', () => { - it('basic', () => { - const types = generateTypes(resolveSchema({ + it('basic', async () => { + const types = generateTypes(await resolveSchema({ test: { foo: { $default: 'test value', @@ -27,8 +27,8 @@ describe('resolveSchema', () => { }" `) }) - it('withOptions', () => { - const types = generateTypes(resolveSchema({ + it('withOptions', async () => { + const types = generateTypes(await resolveSchema({ test: { a: 123, foo: { bar: 123, baz: { x: 123 } } @@ -51,8 +51,8 @@ describe('resolveSchema', () => { `) }) - it('array', () => { - const types = generateTypes(resolveSchema({ + it('array', async () => { + const types = generateTypes(await resolveSchema({ empty: [], numbers: [1, 2, 3], mixed: [true, 123] @@ -71,15 +71,15 @@ describe('resolveSchema', () => { `) }) - it('escapeKey', () => { - const types = generateTypes(resolveSchema({ + it('escapeKey', async () => { + const types = generateTypes(await resolveSchema({ '*key': '123' })) expect(types).toMatch('"*key": string') }) - it('functions', () => { - const types = generateTypes(resolveSchema({ + it('functions', async () => { + const types = generateTypes(await resolveSchema({ add: { $schema: { type: 'function', @@ -104,8 +104,8 @@ export interface Untyped { `.trim()) }) - it('extracts type imports to top-level', () => { - const types = generateTypes(resolveSchema({ + it('extracts type imports to top-level', async () => { + const types = generateTypes(await resolveSchema({ test: { foo: { $schema: { diff --git a/web/app.vue b/web/app.vue index 2bf49f1..0ca70b8 100644 --- a/web/app.vue +++ b/web/app.vue @@ -113,12 +113,12 @@ export default defineComponent({ const transpiledRef = safeComputed(() => loader.transform(state.ref)) const referenceObj = safeComputed(() => evaluateSource(transpiledRef.value)) - const schema = safeComputed(() => resolveSchema(referenceObj.value)) + const schema = safeComputed(async () => await resolveSchema(referenceObj.value)) const types = safeComputed(() => generateTypes(schema.value)) const markdown = safeComputed(() => generateMarkdown(schema.value)) const inputObj = safeComputed(() => evaluateSource(state.input)) - const resolvedInput = safeComputed(() => applyDefaults(referenceObj.value, inputObj.value)) + const resolvedInput = safeComputed(async () => await applyDefaults(referenceObj.value, inputObj.value)) return { state, From 90c7a27e6f8043d6a13feea3c3c1cfbf930d589b Mon Sep 17 00:00:00 2001 From: pooya parsa Date: Mon, 12 Sep 2022 17:05:44 +0200 Subject: [PATCH 2/2] Update src/types.ts Co-authored-by: Daniel Roe --- src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.ts b/src/types.ts index 42aee17..f4bb43d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -55,7 +55,7 @@ export interface Schema extends TypeDescriptor { export interface InputObject { $schema?: Schema - $resolve?: ResolveFn | Promise + $resolve?: ResolveFn $default?: any [key: string]: any }