Skip to content

Commit 9df4763

Browse files
authored
feat: support date and json scalars by default (#1049)
1 parent 7ee63a3 commit 9df4763

File tree

13 files changed

+189
-22
lines changed

13 files changed

+189
-22
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"get-port": "^5.1.0",
5050
"graphql": "^14.5.8",
5151
"graphql-request": "^2.1.0-next.1",
52+
"graphql-scalars": "^1.1.5",
5253
"http-errors": "^1.7.3",
5354
"lodash": "^4.17.15",
5455
"node-fetch": "^2.6.0",

src/lib/nexus-schema-stateful/stateful-nexus-schema.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import * as NexusSchema from '@nexus/schema'
22
import * as GraphQL from 'graphql'
33
import * as CustomTypes from './custom-types'
4+
import * as Scalars from '../scalars'
45

56
// todo use this as return type of constructor
67
export interface StatefulNexusSchema {
78
state: {
89
types: NexusSchemaTypeDef[]
10+
scalars: Scalars.Scalars
911
}
1012
builders: NexusSchemaStatefulBuilders
1113
}
@@ -81,6 +83,7 @@ type NexusSchemaTypeDef =
8183
export function createNexusSchemaStateful() {
8284
const state: StatefulNexusSchema['state'] = {
8385
types: [],
86+
scalars: {},
8487
}
8588

8689
function objectType<TypeName extends string>(
@@ -112,6 +115,7 @@ export function createNexusSchemaStateful() {
112115
): NexusSchema.core.NexusScalarTypeDef<TypeName> {
113116
const typeDef = NexusSchema.scalarType(config)
114117
state.types.push(typeDef)
118+
state.scalars[typeDef.name] = new GraphQL.GraphQLScalarType(config)
115119
return typeDef
116120
}
117121

@@ -159,6 +163,7 @@ export function createNexusSchemaStateful() {
159163
if (type instanceof GraphQL.GraphQLScalarType && methodName) {
160164
const typeDef = NexusSchema.asNexusMethod(type, methodName)
161165
state.types.push(typeDef)
166+
state.scalars[typeDef.name] = typeDef
162167
return typeDef
163168
}
164169

src/lib/plugin/lens.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as Lo from 'lodash'
22
import prompts from 'prompts'
3+
import * as Scalars from '../scalars'
34
import * as Layout from '../layout'
45
import { rootLogger } from '../nexus-logger'
56
import * as Process from '../process'
@@ -17,10 +18,11 @@ export function createBaseLens(pluginName: string): Lens {
1718
}
1819
}
1920

20-
export function createRuntimeLens(pluginName: string): RuntimeLens {
21+
export function createRuntimeLens(pluginName: string, scalars: Scalars.Scalars): RuntimeLens {
2122
return {
2223
...createBaseLens(pluginName),
2324
shouldGenerateArtifacts: isReflectionStage('typegen'),
25+
scalars,
2426
}
2527
}
2628

src/lib/plugin/load.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Either, left, right } from 'fp-ts/lib/Either'
33
import * as Lo from 'lodash'
44
import * as Layout from '../layout'
55
import { rootLogger } from '../nexus-logger'
6+
import * as Scalars from '../scalars'
67
import { partition } from '../utils'
78
import { importPluginDimension } from './import'
89
import { createBaseLens, createRuntimeLens, createWorktimeLens } from './lens'
@@ -14,7 +15,7 @@ const log = rootLogger.child('plugin')
1415
/**
1516
* Fully import and load the runtime plugins, if any, amongst the given plugins.
1617
*/
17-
export function importAndLoadRuntimePlugins(plugins: Plugin[]) {
18+
export function importAndLoadRuntimePlugins(plugins: Plugin[], scalars: Scalars.Scalars) {
1819
const validPlugins = filterValidPlugins(plugins)
1920

2021
const gotManifests = getPluginManifests(validPlugins)
@@ -30,7 +31,7 @@ export function importAndLoadRuntimePlugins(plugins: Plugin[]) {
3031
})
3132
.map((plugin) => {
3233
log.trace('loading runtime plugin', { name: plugin.manifest.name })
33-
return plugin.run(createRuntimeLens(plugin.manifest.name))
34+
return plugin.run(createRuntimeLens(plugin.manifest.name, scalars))
3435
})
3536
}
3637

src/lib/plugin/types/lens.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as Logger from '@nexus/logger'
22
import * as NexusSchema from '@nexus/schema'
33
import * as Prompts from 'prompts'
4+
import * as Scalars from '../../scalars'
45
import * as Testing from '../../../testing/testing'
56
import * as Layout from '../../layout'
67
import * as PackageManager from '../../package-manager'
@@ -147,6 +148,7 @@ export type Lens = {
147148

148149
export interface RuntimeLens extends Lens {
149150
shouldGenerateArtifacts: boolean // TODO: Should probably become isReflectionPhase
151+
scalars: Scalars.Scalars
150152
}
151153

152154
export interface TesttimeLens extends Lens {}

src/lib/reflection/fork-script.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ async function run() {
2626

2727
if (isReflectionStage('typegen')) {
2828
try {
29-
const plugins = Plugin.importAndLoadRuntimePlugins(app.private.state.plugins)
29+
const plugins = Plugin.importAndLoadRuntimePlugins(
30+
app.private.state.plugins,
31+
app.private.state.schemaComponent.scalars
32+
)
3033
await writeArtifacts({
3134
graphqlSchema: app.private.state.assembled!.schema,
3235
layout,

src/lib/scalars/index.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { DateTimeResolver, JSONObjectResolver } from 'graphql-scalars'
2+
import { GraphQLScalarType } from 'graphql'
3+
4+
export type Scalars = Record<string, GraphQLScalarType>
5+
6+
interface BuiltinScalars {
7+
Date: GraphQLScalarType
8+
Json: GraphQLScalarType
9+
[x: string]: GraphQLScalarType
10+
}
11+
12+
export const builtinScalars: BuiltinScalars = {
13+
Date: new GraphQLScalarType({
14+
...DateTimeResolver,
15+
name: 'Date',
16+
}),
17+
Json: new GraphQLScalarType({
18+
...JSONObjectResolver,
19+
name: 'Json',
20+
description:
21+
'The `JSON` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).',
22+
}),
23+
}

src/runtime/app.ts

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import * as Server from './server'
1010
import { ContextCreator } from './server/server'
1111
import * as Settings from './settings'
1212
import { assertAppNotAssembled } from './utils'
13+
import { builtinScalars } from '../lib/scalars'
1314

1415
const log = Logger.log.child('app')
1516

@@ -120,17 +121,7 @@ export function create(): App {
120121
log: Logger.log,
121122
})
122123

123-
/**
124-
* Setup default log filter
125-
*/
126-
127-
settingsComponent.public.change({
128-
logger: {
129-
filter: 'app:*, nexus:*@info+, *@warn+',
130-
},
131-
})
132-
133-
const api: App = {
124+
const app: App = {
134125
log: log,
135126
settings: settingsComponent.public,
136127
schema: schemaComponent.public,
@@ -148,6 +139,8 @@ export function create(): App {
148139
assemble() {
149140
if (appState.assembled) return
150141

142+
schemaComponent.private.beforeAssembly()
143+
151144
/**
152145
* Plugin reflection is run in the same process (eval). This means if the
153146
* process is the app, which it is during testing for example, then we
@@ -159,7 +152,10 @@ export function create(): App {
159152

160153
appState.assembled = {} as AppState['assembled']
161154

162-
const loadedPlugins = Plugin.importAndLoadRuntimePlugins(appState.plugins)
155+
const loadedPlugins = Plugin.importAndLoadRuntimePlugins(
156+
appState.plugins,
157+
appState.schemaComponent.scalars
158+
)
163159
appState.assembled!.loadedPlugins = loadedPlugins
164160

165161
const { schema, missingTypes } = schemaComponent.private.assemble(loadedPlugins)
@@ -194,8 +190,23 @@ export function create(): App {
194190
},
195191
}
196192

193+
/**
194+
* Setup default log filter
195+
*/
196+
app.settings.change({
197+
logger: {
198+
filter: 'app:*, nexus:*@info+, *@warn+',
199+
},
200+
})
201+
202+
/**
203+
* Setup default scalar types
204+
*/
205+
app.schema.importType(builtinScalars.Date, 'date')
206+
app.schema.importType(builtinScalars.Json, 'json')
207+
197208
return {
198-
...api,
209+
...app,
199210
private: {
200211
state: appState,
201212
},

src/runtime/schema/schema.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as NexusLogger from '@nexus/logger'
22
import * as NexusSchema from '@nexus/schema'
3-
import { GraphQLFieldResolver, GraphQLResolveInfo } from 'graphql'
3+
import { GraphQLFieldResolver, GraphQLResolveInfo, GraphQLScalarType } from 'graphql'
44
import * as HTTP from 'http'
55
import { createNexusSchemaStateful, NexusSchemaStatefulBuilders } from '../../lib/nexus-schema-stateful'
66
import { RuntimeContributions } from '../../lib/plugin'
@@ -10,16 +10,19 @@ import { assertAppNotAssembled } from '../utils'
1010
import { log } from './logger'
1111
import { createSchemaSettingsManager, SchemaSettingsManager } from './settings'
1212
import { mapSettingsAndPluginsToNexusSchemaConfig } from './settings-mapper'
13+
import * as Scalars from '../../lib/scalars'
1314

1415
export type LazyState = {
1516
contextContributors: ContextContributor[]
1617
plugins: NexusSchema.core.NexusPlugin[]
18+
scalars: Scalars.Scalars
1719
}
1820

1921
function createLazyState(): LazyState {
2022
return {
2123
contextContributors: [],
2224
plugins: [],
25+
scalars: {},
2326
}
2427
}
2528

@@ -72,6 +75,7 @@ export interface SchemaInternal {
7275
assemble(
7376
plugins: RuntimeContributions[]
7477
): { schema: NexusSchema.core.NexusGraphQLSchema; missingTypes: Index<NexusSchema.core.MissingType> }
78+
beforeAssembly(): void
7579
reset(): void
7680
}
7781
public: Schema
@@ -110,10 +114,15 @@ export function create(appState: AppState): SchemaInternal {
110114
settings: settings,
111115
reset() {
112116
statefulNexusSchema.state.types = []
117+
statefulNexusSchema.state.scalars = {}
113118
appState.schemaComponent.contextContributors = []
114119
appState.schemaComponent.plugins = []
120+
appState.schemaComponent.scalars = {}
115121
},
116-
assemble: (plugins) => {
122+
beforeAssembly() {
123+
appState.schemaComponent.scalars = statefulNexusSchema.state.scalars
124+
},
125+
assemble(plugins) {
117126
const nexusSchemaConfig = mapSettingsAndPluginsToNexusSchemaConfig(plugins, settings.data)
118127
nexusSchemaConfig.types.push(...statefulNexusSchema.state.types)
119128
nexusSchemaConfig.plugins!.push(...appState.schemaComponent.plugins)
@@ -122,7 +131,12 @@ export function create(appState: AppState): SchemaInternal {
122131
},
123132
checks() {
124133
NexusSchema.core.assertNoMissingTypes(appState.assembled!.schema, appState.assembled!.missingTypes)
125-
if (statefulNexusSchema.state.types.length === 0) {
134+
135+
// TODO: We should separate types added by the framework and the ones added by users
136+
if (
137+
statefulNexusSchema.state.types.length === 2 &&
138+
statefulNexusSchema.state.types.every((t) => Scalars.builtinScalars[t.name] !== undefined)
139+
) {
126140
log.warn(emptyExceptionMessage())
127141
}
128142
},

src/runtime/schema/settings.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export type SettingsInput = {
6060
/**
6161
* Disable or configure the authorization plugin
6262
*/
63-
authorization?: false | NexusSchema.core.FieldAuthorizePluginConfig,
63+
authorization?: false | NexusSchema.core.FieldAuthorizePluginConfig
6464
/**
6565
* Should a [GraphQL SDL file](https://www.prisma.io/blog/graphql-sdl-schema-definition-language-6755bcb9ce51) be generated when the app is built and to where?
6666
*
@@ -171,7 +171,7 @@ function defaultSettings(): SettingsData {
171171
...connectionPluginConfigManagedByNexus,
172172
},
173173
},
174-
authorization: {}
174+
authorization: {},
175175
}
176176

177177
return data

test/system/__snapshots__/create-prisma-app.test.ts.snap

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,26 @@ In some cases, you need to provide options to alter GraphQL's execution behavior
10341034
"name": "__DirectiveLocation",
10351035
"possibleTypes": null,
10361036
},
1037+
Object {
1038+
"description": "Use JavaScript Date object for date/time fields.",
1039+
"enumValues": null,
1040+
"fields": null,
1041+
"inputFields": null,
1042+
"interfaces": null,
1043+
"kind": "SCALAR",
1044+
"name": "Date",
1045+
"possibleTypes": null,
1046+
},
1047+
Object {
1048+
"description": "The \`JSON\` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).",
1049+
"enumValues": null,
1050+
"fields": null,
1051+
"inputFields": null,
1052+
"interfaces": null,
1053+
"kind": "SCALAR",
1054+
"name": "Json",
1055+
"possibleTypes": null,
1056+
},
10371057
],
10381058
},
10391059
}

0 commit comments

Comments
 (0)