From 642be8b69fa411e4a376553f1692ac2cb1b81834 Mon Sep 17 00:00:00 2001 From: Flavian DESVERNE Date: Tue, 8 Dec 2020 19:16:07 +0100 Subject: [PATCH] feat: make contextType independent of source types BREAKING CHANGES: - `contextType` is no longer nested in `typegenAutoConfig` - `contextType` can no longer references `sources` registered in `typegenAutoConfig` - `contextType` API was refactored to use `module` instead of `path` and `export` instead of `name` - `rootTyping` API was refactored to use `module` instead of `path` and `export` instead of `name` --- examples/apollo-fullstack/src/context.ts | 8 ++ .../apollo-fullstack/src/fullstack-typegen.ts | 3 +- examples/apollo-fullstack/src/index.ts | 9 +- examples/apollo-fullstack/src/typeDefs.ts | 7 -- examples/ghost/config.development.json | 2 +- examples/ghost/scripts/dumpTables.ts | 9 +- examples/ghost/src/generated/ghost-nexus.ts | 103 +++++++++++++++--- examples/ghost/src/ghost-schema.ts | 9 +- .../src/kitchen-sink-definitions.ts | 2 +- examples/kitchen-sink/src/kitchen-sink.gen.ts | 1 + examples/star-wars/src/schema.ts | 7 +- examples/star-wars/src/star-wars-typegen.ts | 3 +- examples/star-wars/src/types/backingTypes.ts | 26 ++--- examples/star-wars/src/types/context.ts | 1 + examples/ts-ast-reader/src/schema.ts | 5 +- .../src/ts-ast-reader-typegen.ts | 4 +- examples/ts-ast-reader/src/types/context.ts | 5 + examples/ts-ast-reader/src/types/index.ts | 4 - src/builder.ts | 11 +- src/definitions/_types.ts | 8 +- src/definitions/objectType.ts | 8 +- src/typegenAutoConfig.ts | 24 +--- src/typegenMetadata.ts | 19 ++-- src/typegenPrinter.ts | 16 +-- src/utils.ts | 16 ++- .../__snapshots__/typegenPrinter.spec.ts.snap | 5 +- tests/backingTypes.spec.ts | 27 +++-- tests/makeSchema.spec.ts | 55 +++++++++- tests/scalarType.spec.ts | 4 +- tests/typegenPrinter.spec.ts | 7 +- 30 files changed, 274 insertions(+), 134 deletions(-) create mode 100644 examples/apollo-fullstack/src/context.ts create mode 100644 examples/star-wars/src/types/context.ts create mode 100644 examples/ts-ast-reader/src/types/context.ts diff --git a/examples/apollo-fullstack/src/context.ts b/examples/apollo-fullstack/src/context.ts new file mode 100644 index 00000000..a7d5b218 --- /dev/null +++ b/examples/apollo-fullstack/src/context.ts @@ -0,0 +1,8 @@ +import { LaunchApi, UserApi } from './typeDefs' + +export interface Context { + dataSources: { + userAPI: UserApi + launchAPI: LaunchApi + } +} diff --git a/examples/apollo-fullstack/src/fullstack-typegen.ts b/examples/apollo-fullstack/src/fullstack-typegen.ts index 0088c424..9ece8315 100644 --- a/examples/apollo-fullstack/src/fullstack-typegen.ts +++ b/examples/apollo-fullstack/src/fullstack-typegen.ts @@ -1,6 +1,7 @@ /** This file was generated by Nexus Schema Do not make changes to this file directly */ import * as t from './typeDefs' +import { Context } from './context' declare global { interface NexusGen extends NexusGenTypes {} @@ -221,7 +222,7 @@ export type NexusGenFeaturesConfig = { } export interface NexusGenTypes { - context: t.Context + context: Context inputTypes: NexusGenInputs rootTypes: NexusGenRootTypes inputTypeShapes: NexusGenInputs & NexusGenEnums & NexusGenScalars diff --git a/examples/apollo-fullstack/src/index.ts b/examples/apollo-fullstack/src/index.ts index 7e993b9b..4b303b76 100644 --- a/examples/apollo-fullstack/src/index.ts +++ b/examples/apollo-fullstack/src/index.ts @@ -15,16 +15,19 @@ const schema = makeSchema({ types, outputs: { schema: path.join(__dirname, '../fullstack-schema.graphql'), - typegen: path.join(__dirname.replace(/\/dist$/, '/src'), '../src/fullstack-typegen.ts'), + typegen: path.join(__dirname, 'fullstack-typegen.ts'), }, typegenAutoConfig: { sources: [ { - source: path.join(__dirname.replace(/\/dist$/, '/src'), './typeDefs.ts'), + source: path.join(__dirname, 'typeDefs.ts'), alias: 't', }, ], - contextType: 't.Context', + }, + contextType: { + module: path.join(__dirname, 'context.ts'), + export: 'Context', }, prettierConfig: require.resolve('../../../.prettierrc'), }) diff --git a/examples/apollo-fullstack/src/typeDefs.ts b/examples/apollo-fullstack/src/typeDefs.ts index 3af64a7e..010f2829 100644 --- a/examples/apollo-fullstack/src/typeDefs.ts +++ b/examples/apollo-fullstack/src/typeDefs.ts @@ -65,10 +65,3 @@ export interface Utils { }): T[] createStore(): {} } - -export interface Context { - dataSources: { - userAPI: UserApi - launchAPI: LaunchApi - } -} diff --git a/examples/ghost/config.development.json b/examples/ghost/config.development.json index 6d54d452..f1097ac5 100644 --- a/examples/ghost/config.development.json +++ b/examples/ghost/config.development.json @@ -9,7 +9,7 @@ "host": "127.0.0.1", "port": 3306, "user": "root", - "password": "", + "password": "root", "database": "ghost_nexus" } } diff --git a/examples/ghost/scripts/dumpTables.ts b/examples/ghost/scripts/dumpTables.ts index 2977c7f2..89738e3d 100644 --- a/examples/ghost/scripts/dumpTables.ts +++ b/examples/ghost/scripts/dumpTables.ts @@ -5,8 +5,10 @@ import fs from 'fs-extra' import _ from 'lodash' import Knex from 'knex' +const config = require('../config.development.json') + const knex = Knex({ - ...require('../config.development.json').database, + ...config.database, }) const GENERATED_DIR = path.join(__dirname, '..', 'src', 'generated') @@ -14,7 +16,10 @@ const OUT_TYPES = path.join(GENERATED_DIR, 'ghost-db-types.ts') const OUT_TABLES = path.join(GENERATED_DIR, 'ghost-db-tables.ts') async function generateTypes() { - const rows = await knex.select('*').from('information_schema.tables').where('table_schema', 'ghost_nexus') + const rows = await knex + .select('*') + .from('information_schema.tables') + .where('table_schema', config.database.connection.database) const tableNames = _.map(rows, 'TABLE_NAME').sort() const connectionString = dbConnectionString() await knex.destroy() diff --git a/examples/ghost/src/generated/ghost-nexus.ts b/examples/ghost/src/generated/ghost-nexus.ts index e5643078..aa42b090 100644 --- a/examples/ghost/src/generated/ghost-nexus.ts +++ b/examples/ghost/src/generated/ghost-nexus.ts @@ -1,22 +1,27 @@ -/** - * This file was generated by Nexus Schema - * Do not make changes to this file directly - */ +/** This file was generated by Nexus Schema Do not make changes to this file directly */ -import * as ctx from '../data-sources/Context' import * as db from './ghost-db-types' +import { Context } from './../data-sources/Context' import { FieldAuthorizeResolver } from '@nexus/schema/dist/plugins/fieldAuthorizePlugin' import { core } from '@nexus/schema' declare global { interface NexusGenCustomInputMethods { + /** + * A date string, such as 2007-12-03, compliant with the `full-date` format outlined in section 5.6 of the + * RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar. + */ date( fieldName: FieldName, - opts?: core.ScalarInputFieldConfig> + opts?: core.CommonInputFieldConfig ): void // "Date"; } } declare global { interface NexusGenCustomOutputMethods { + /** + * A date string, such as 2007-12-03, compliant with the `full-date` format outlined in section 5.6 of the + * RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar. + */ date( fieldName: FieldName, ...opts: core.ScalarOutSpread @@ -178,16 +183,17 @@ export type NexusGenAbstractsUsingStrategyResolveType = never export type NexusGenFeaturesConfig = { abstractTypeStrategies: { - isTypeOf: true - resolveType: false + isTypeOf: false + resolveType: true __typename: false } } export interface NexusGenTypes { - context: ctx.Context + context: Context inputTypes: NexusGenInputs rootTypes: NexusGenRootTypes + inputTypeShapes: NexusGenInputs & NexusGenEnums & NexusGenScalars argTypes: NexusGenArgTypes fieldTypes: NexusGenFieldTypes fieldTypeNames: NexusGenFieldTypeNames @@ -218,14 +224,81 @@ declare global { interface NexusGenPluginTypeConfig {} interface NexusGenPluginFieldConfig { /** - * Authorization for an individual field. Returning "true" - * or "Promise" means the field can be accessed. - * Returning "false" or "Promise" will respond - * with a "Not Authorized" error for the field. - * Returning or throwing an error will also prevent the - * resolver from executing. + * Authorization for an individual field. Returning "true" or "Promise" means the field can be + * accessed. Returning "false" or "Promise" will respond with a "Not Authorized" error for the + * field. Returning or throwing an error will also prevent the resolver from executing. */ authorize?: FieldAuthorizeResolver + /** + * Whether the type can be null + * + * @default (depends on whether nullability is configured in type or schema) + * @see declarativeWrappingPlugin + */ + nullable?: boolean + /** + * Whether the type is list of values, or just a single value. If list is true, we assume the type is a + * list. If list is an array, we'll assume that it's a list with the depth. The boolean indicates whether + * the type is required (non-null), where true = nonNull, false = nullable. + * + * @see declarativeWrappingPlugin + */ + list?: true | boolean[] + /** + * Whether the type should be non null, `required: true` = `nullable: false` + * + * @default (depends on whether nullability is configured in type or schema) + * @see declarativeWrappingPlugin + */ + required?: boolean + } + interface NexusGenPluginInputFieldConfig { + /** + * Whether the type can be null + * + * @default (depends on whether nullability is configured in type or schema) + * @see declarativeWrappingPlugin + */ + nullable?: boolean + /** + * Whether the type is list of values, or just a single value. If list is true, we assume the type is a + * list. If list is an array, we'll assume that it's a list with the depth. The boolean indicates whether + * the type is required (non-null), where true = nonNull, false = nullable. + * + * @see declarativeWrappingPlugin + */ + list?: true | boolean[] + /** + * Whether the type should be non null, `required: true` = `nullable: false` + * + * @default (depends on whether nullability is configured in type or schema) + * @see declarativeWrappingPlugin + */ + required?: boolean } interface NexusGenPluginSchemaConfig {} + interface NexusGenPluginArgConfig { + /** + * Whether the type can be null + * + * @default (depends on whether nullability is configured in type or schema) + * @see declarativeWrappingPlugin + */ + nullable?: boolean + /** + * Whether the type is list of values, or just a single value. If list is true, we assume the type is a + * list. If list is an array, we'll assume that it's a list with the depth. The boolean indicates whether + * the type is required (non-null), where true = nonNull, false = nullable. + * + * @see declarativeWrappingPlugin + */ + list?: true | boolean[] + /** + * Whether the type should be non null, `required: true` = `nullable: false` + * + * @default (depends on whether nullability is configured in type or schema) + * @see declarativeWrappingPlugin + */ + required?: boolean + } } diff --git a/examples/ghost/src/ghost-schema.ts b/examples/ghost/src/ghost-schema.ts index 1723d22a..316232bb 100644 --- a/examples/ghost/src/ghost-schema.ts +++ b/examples/ghost/src/ghost-schema.ts @@ -10,12 +10,7 @@ export const schema = makeSchema({ }, plugins: [fieldAuthorizePlugin()], typegenAutoConfig: { - contextType: 'ctx.Context', sources: [ - { - alias: 'ctx', - source: path.join(__dirname, 'data-sources', 'Context.ts'), - }, { alias: 'db', source: path.join(__dirname, 'generated', 'ghost-db-types.ts'), @@ -26,5 +21,9 @@ export const schema = makeSchema({ Date: 'Date', }, }, + contextType: { + module: path.join(__dirname, 'data-sources', 'Context.ts'), + export: 'Context', + }, prettierConfig: require.resolve('../../../.prettierrc'), }) diff --git a/examples/kitchen-sink/src/kitchen-sink-definitions.ts b/examples/kitchen-sink/src/kitchen-sink-definitions.ts index b4701574..cde1d6b5 100644 --- a/examples/kitchen-sink/src/kitchen-sink-definitions.ts +++ b/examples/kitchen-sink/src/kitchen-sink-definitions.ts @@ -83,7 +83,7 @@ export const UnusedInterface = interfaceType({ definition(t) { t.boolean('ok') }, - rootTyping: { name: 'UnusedInterfaceTypeDef', path: __filename }, + rootTyping: { module: __filename, export: 'UnusedInterfaceTypeDef' }, }) export const Baz = interfaceType({ diff --git a/examples/kitchen-sink/src/kitchen-sink.gen.ts b/examples/kitchen-sink/src/kitchen-sink.gen.ts index ba18f6e7..5c7216a6 100644 --- a/examples/kitchen-sink/src/kitchen-sink.gen.ts +++ b/examples/kitchen-sink/src/kitchen-sink.gen.ts @@ -115,6 +115,7 @@ export interface NexusGenObjects { SomeNode: { // root type data?: NexusGenRootTypes['SomeNode'] | null // SomeNode + id?: string | null // ID } TestObj: { // root type diff --git a/examples/star-wars/src/schema.ts b/examples/star-wars/src/schema.ts index 8b439eef..9835bb22 100644 --- a/examples/star-wars/src/schema.ts +++ b/examples/star-wars/src/schema.ts @@ -22,11 +22,14 @@ export const schema = makeSchema({ typegenAutoConfig: { sources: [ { - source: path.join(__dirname.replace(/\/dist$/, '/src'), './types/backingTypes.ts'), + source: path.join(__dirname, 'types', 'backingTypes.ts'), alias: 'swapi', }, ], - contextType: 'swapi.ContextType', + }, + contextType: { + module: path.join(__dirname, 'types', 'context.ts'), + export: 'ContextType', }, prettierConfig: require.resolve('../../../.prettierrc'), features: { diff --git a/examples/star-wars/src/star-wars-typegen.ts b/examples/star-wars/src/star-wars-typegen.ts index 74e9beeb..a4a52401 100644 --- a/examples/star-wars/src/star-wars-typegen.ts +++ b/examples/star-wars/src/star-wars-typegen.ts @@ -1,6 +1,7 @@ /** This file was generated by Nexus Schema Do not make changes to this file directly */ import * as swapi from './types/backingTypes' +import { ContextType } from './types/context' declare global { interface NexusGen extends NexusGenTypes {} @@ -170,7 +171,7 @@ export type NexusGenFeaturesConfig = { } export interface NexusGenTypes { - context: swapi.ContextType + context: ContextType inputTypes: NexusGenInputs rootTypes: NexusGenRootTypes inputTypeShapes: NexusGenInputs & NexusGenEnums & NexusGenScalars diff --git a/examples/star-wars/src/types/backingTypes.ts b/examples/star-wars/src/types/backingTypes.ts index 4272fd2a..4dd694ac 100644 --- a/examples/star-wars/src/types/backingTypes.ts +++ b/examples/star-wars/src/types/backingTypes.ts @@ -1,26 +1,24 @@ -import { NexusGenEnums } from "../star-wars-typegen"; +import { NexusGenEnums } from '../star-wars-typegen' /** - * These are Flow types which correspond to the schema. - * They represent the shape of the data visited during field resolution. + * These are Flow types which correspond to the schema. They represent the shape of the data visited during + * field resolution. */ export interface CharacterFields { - id: string; - name: string; - friends: string[]; - appears_in: NexusGenEnums["Episode"][]; + id: string + name: string + friends: string[] + appears_in: NexusGenEnums['Episode'][] } export interface Human extends CharacterFields { - type: "Human"; - home_planet?: string; + type: 'Human' + home_planet?: string } export interface Droid extends CharacterFields { - type: "Droid"; - primary_function: string; + type: 'Droid' + primary_function: string } -export type Character = Human | Droid; - -export interface ContextType {} +export type Character = Human | Droid diff --git a/examples/star-wars/src/types/context.ts b/examples/star-wars/src/types/context.ts new file mode 100644 index 00000000..6be4ce9a --- /dev/null +++ b/examples/star-wars/src/types/context.ts @@ -0,0 +1 @@ +export interface ContextType {} diff --git a/examples/ts-ast-reader/src/schema.ts b/examples/ts-ast-reader/src/schema.ts index 71e9d8d9..95076dc5 100644 --- a/examples/ts-ast-reader/src/schema.ts +++ b/examples/ts-ast-reader/src/schema.ts @@ -23,12 +23,15 @@ export const schema = makeSchema({ onlyTypes: [], }, ], - contextType: 't.ContextType', backingTypeMap: { Token: 'ts.Token', }, // debug: true, }, + contextType: { + module: path.join(__dirname, './types/context.ts'), + export: 'ContextType', + }, prettierConfig: require.resolve('../../../.prettierrc'), nonNullDefaults: { output: true, diff --git a/examples/ts-ast-reader/src/ts-ast-reader-typegen.ts b/examples/ts-ast-reader/src/ts-ast-reader-typegen.ts index 22b36c81..55a16868 100644 --- a/examples/ts-ast-reader/src/ts-ast-reader-typegen.ts +++ b/examples/ts-ast-reader/src/ts-ast-reader-typegen.ts @@ -1,7 +1,7 @@ /** This file was generated by Nexus Schema Do not make changes to this file directly */ -import * as t from './types/index' import ts from 'typescript' +import { ContextType } from './types/context' declare global { interface NexusGen extends NexusGenTypes {} @@ -2875,7 +2875,7 @@ export type NexusGenFeaturesConfig = { } export interface NexusGenTypes { - context: t.ContextType + context: ContextType inputTypes: NexusGenInputs rootTypes: NexusGenRootTypes inputTypeShapes: NexusGenInputs & NexusGenEnums & NexusGenScalars diff --git a/examples/ts-ast-reader/src/types/context.ts b/examples/ts-ast-reader/src/types/context.ts new file mode 100644 index 00000000..17b60fc2 --- /dev/null +++ b/examples/ts-ast-reader/src/types/context.ts @@ -0,0 +1,5 @@ +import * as ts from 'typescript' + +export interface ContextType { + source: ts.SourceFile +} diff --git a/examples/ts-ast-reader/src/types/index.ts b/examples/ts-ast-reader/src/types/index.ts index b08251c7..e4dbf716 100644 --- a/examples/ts-ast-reader/src/types/index.ts +++ b/examples/ts-ast-reader/src/types/index.ts @@ -11,10 +11,6 @@ export * from './objects' export * from './unions' export * from './jsdoc' -export interface ContextType { - source: ts.SourceFile -} - export const Query = objectType({ name: 'Query', definition(t) { diff --git a/src/builder.ts b/src/builder.ts index 81be7d91..b1faa6d5 100644 --- a/src/builder.ts +++ b/src/builder.ts @@ -242,6 +242,13 @@ export interface BuilderConfigInput { customPrintSchemaFn?: typeof printSchema /** Customize and toggle on or off various features of Nexus. */ features?: NexusFeaturesInput + /** + * Path to the module where your context type is exported + * + * @example + * contextType: { module: path.join(__dirname, 'context.ts'), export: 'MyContextType' } + */ + contextType?: TypingImport } export interface BuilderConfig extends Omit { @@ -277,10 +284,8 @@ export interface TypegenInfo { imports: string[] /** A map of all GraphQL types and what TypeScript types they should be represented by. */ backingTypeMap: { [K in GetGen<'objectNames'>]?: string } - /** The string type of the context for the resolvers */ - contextType?: string /** Info about where to import the context from */ - contextTypeImport?: TypingImport + contextTypeImport: TypingImport | undefined /** * The path to the @nexus/schema package for typegen. * diff --git a/src/definitions/_types.ts b/src/definitions/_types.ts index b488fcbd..5bbc8c68 100644 --- a/src/definitions/_types.ts +++ b/src/definitions/_types.ts @@ -104,10 +104,10 @@ export type RootTypingDef = string | TypingImport export type RootTypings = Record export interface TypingImport { - /** The name of a package installed in your project or absolute path to a module in your project. */ - path: string - /** The name of a type being exported from the package/module (specified in path) that you want to use. */ - name: string + /** An absolute path to a module in your project or the name of a package installed in your project. */ + module: string + /** The name of a type exported from the module/package (specified in `module`) that you want to use. */ + export: string /** * The name you want the imported type to be referenced as in the typegen. * diff --git a/src/definitions/objectType.ts b/src/definitions/objectType.ts index 746a7048..fee093d8 100644 --- a/src/definitions/objectType.ts +++ b/src/definitions/objectType.ts @@ -118,14 +118,14 @@ export type NexusObjectTypeConfig = { * * @example * { - * path: 'some-package', - * name: 'User', + * module: 'some-package', + * export: 'User', * } * * @example * { - * path: `${__dirname}/some/module.ts`, - * name: 'User', + * module: `${__dirname}/some/module.ts`, + * export: 'User', * } */ rootTyping?: RootTypingDef diff --git a/src/typegenAutoConfig.ts b/src/typegenAutoConfig.ts index 7ad6e246..bc5d3b43 100644 --- a/src/typegenAutoConfig.ts +++ b/src/typegenAutoConfig.ts @@ -1,7 +1,7 @@ import { GraphQLNamedType, GraphQLSchema, isOutputType } from 'graphql' import * as path from 'path' import { TypegenInfo } from './builder' -import { RootTypingDef, TypingImport } from './definitions/_types' +import { TypingImport } from './definitions/_types' import { TYPEGEN_HEADER } from './lang' import { getOwnPackage, log, objValues, relativePathTo, typeScriptFileExtension } from './utils' @@ -60,13 +60,6 @@ export interface TypegenAutoConfigOptions { * ] */ sources: TypegenConfigSourceModule[] - /** - * Typing for the context, referencing a type defined in the aliased module provided in sources e.g. `alias.Context` - * - * @example - * contextType: 't.Context' - */ - contextType?: RootTypingDef /** * Types that should not be matched for a backing type, * @@ -97,11 +90,10 @@ export interface TypegenAutoConfigOptions { * * @param options */ -export function typegenAutoConfig(options: TypegenAutoConfigOptions) { +export function typegenAutoConfig(options: TypegenAutoConfigOptions, contextType: TypingImport | undefined) { return async (schema: GraphQLSchema, outputPath: string): Promise => { const { headers, - contextType, skipTypes = ['Query', 'Mutation', 'Subscription'], backingTypeMap: _backingTypeMap, debug, @@ -272,21 +264,11 @@ export function typegenAutoConfig(options: TypegenAutoConfigOptions) { imports.push(`import ${glob ? '* as ' : ''}${alias} from "${safeImportPath}"`) }) - let contextTypeImport: TypingImport | undefined - let contextTypeString: string | undefined - if (typeof contextType === 'string') { - contextTypeString = contextType - } else if (contextType) { - contextTypeString = contextType.alias ?? contextType.name - contextTypeImport = contextType - } - const typegenInfo = { headers: headers || [TYPEGEN_HEADER], backingTypeMap, imports, - contextType: contextTypeString, - contextTypeImport, + contextTypeImport: contextType, nexusSchemaImportId: getOwnPackage().name, } diff --git a/src/typegenMetadata.ts b/src/typegenMetadata.ts index 64a073bb..8d365e4a 100644 --- a/src/typegenMetadata.ts +++ b/src/typegenMetadata.ts @@ -111,32 +111,37 @@ export class TypegenMetadata { } /** Generates the type definitions */ - async generateTypesFile(schema: NexusGraphQLSchema, typegenFile: string): Promise { + async generateTypesFile(schema: NexusGraphQLSchema, typegenPath: string): Promise { + const typegenInfo = await this.getTypegenInfo(schema, typegenPath) + return new TypegenPrinter(schema, { - ...(await this.getTypegenInfo(schema)), - typegenFile, + ...typegenInfo, + typegenPath, }).print() } - async getTypegenInfo(schema: GraphQLSchema): Promise { + async getTypegenInfo(schema: GraphQLSchema, typegenPath?: string): Promise { if (this.config.typegenConfig) { if (this.config.typegenAutoConfig) { console.warn( `Only one of typegenConfig and typegenAutoConfig should be specified, ignoring typegenConfig` ) } - return this.config.typegenConfig(schema, this.config.outputs.typegen || '') + return this.config.typegenConfig(schema, typegenPath || this.config.outputs.typegen || '') } if (this.config.typegenAutoConfig) { - return typegenAutoConfig(this.config.typegenAutoConfig)(schema, this.config.outputs.typegen || '') + return typegenAutoConfig(this.config.typegenAutoConfig, this.config.contextType)( + schema, + typegenPath || this.config.outputs.typegen || '' + ) } return { nexusSchemaImportId: this.config.nexusSchemaImportId, headers: [TYPEGEN_HEADER], imports: [], - contextType: 'any', + contextTypeImport: this.config.contextType, backingTypeMap: {}, } } diff --git a/src/typegenPrinter.ts b/src/typegenPrinter.ts index 3bb18643..40202e5e 100644 --- a/src/typegenPrinter.ts +++ b/src/typegenPrinter.ts @@ -51,7 +51,7 @@ type TypeMapping = Record type RootTypeMapping = Record> interface TypegenInfoWithFile extends TypegenInfo { - typegenFile: string + typegenPath: string } /** @@ -164,7 +164,7 @@ export class TypegenPrinter { const { contextTypeImport } = this.typegenInfo const imports: string[] = [] const importMap: Record> = {} - const outputPath = this.typegenInfo.typegenFile + const outputPath = this.typegenInfo.typegenPath const nexusSchemaImportId = this.typegenInfo.nexusSchemaImportId ?? getOwnPackage().name if (!this.printImports[nexusSchemaImportId]) { @@ -183,8 +183,8 @@ export class TypegenPrinter { importMap[importPath] = importMap[importPath] || new Set() importMap[importPath].add( contextTypeImport.alias - ? `${contextTypeImport.name} as ${contextTypeImport.alias}` - : contextTypeImport.name + ? `${contextTypeImport.export} as ${contextTypeImport.alias}` + : contextTypeImport.export ) } @@ -192,7 +192,9 @@ export class TypegenPrinter { if (typeof rootType !== 'string') { const importPath = resolveImportPath(rootType, typeName, outputPath) importMap[importPath] = importMap[importPath] || new Set() - importMap[importPath].add(rootType.alias ? `${rootType.name} as ${rootType.alias}` : rootType.name) + importMap[importPath].add( + rootType.alias ? `${rootType.export} as ${rootType.alias}` : rootType.export + ) } }) eachObj(importMap, (val, key) => { @@ -332,7 +334,7 @@ export class TypegenPrinter { } printContext() { - return this.typegenInfo.contextType || '{}' + return this.typegenInfo.contextTypeImport?.alias || this.typegenInfo.contextTypeImport?.export || 'any' } buildResolveSourceTypeMap() { @@ -553,7 +555,7 @@ export class TypegenPrinter { resolveBackingType(typeName: string): string | undefined { const rootTyping = this.schema.extensions.nexus.config.rootTypings[typeName] if (rootTyping) { - return typeof rootTyping === 'string' ? rootTyping : rootTyping.name + return typeof rootTyping === 'string' ? rootTyping : rootTyping.export } return (this.typegenInfo.backingTypeMap as any)[typeName] } diff --git a/src/utils.ts b/src/utils.ts index 471c502f..ccf78598 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -236,14 +236,14 @@ function nixifyPathSlashes(path: string): string { * * Do not pass Node module IDs here as they will be treated as relative paths e.g. "react" "@types/react" etc. */ -export function formatPathForModuleimport(path: string) { +export function formatPathForModuleImport(path: string) { return nixifyPathSlashes(makeRelativePathExplicitlyRelative(path).replace(typeScriptFileExtension, '')) } export function relativePathTo(absolutePath: string, fromPath: string): string { const filename = Path.basename(absolutePath) const relative = Path.relative(Path.dirname(fromPath), Path.dirname(absolutePath)) - return formatPathForModuleimport(Path.join(relative, filename)) + return formatPathForModuleImport(Path.join(relative, filename)) } export interface PrintedGenTypingImportConfig { @@ -486,7 +486,7 @@ function isNodeModule(path: string) { } export function resolveImportPath(rootType: TypingImport, typeName: string, outputPath: string) { - const rootTypePath = rootType.path + const rootTypePath = rootType.module if (typeof rootTypePath !== 'string' || (!Path.isAbsolute(rootTypePath) && !isNodeModule(rootTypePath))) { throw new Error( @@ -504,9 +504,15 @@ export function resolveImportPath(rootType: TypingImport, typeName: string, outp throw new Error(`Root typing path ${rootTypePath} for the type ${typeName} does not exist`) } - const importPath = isNodeModule(rootTypePath) ? rootTypePath : relativePathTo(rootTypePath, outputPath) + if (isNodeModule(rootTypePath)) { + return rootTypePath + } + + if (Path.isAbsolute(rootTypePath)) { + return relativePathTo(rootTypePath, outputPath) + } - return importPath + return rootTypePath } /** Given the right hand side of an arg definition, returns the underlying "named type" for us to add to the builder */ diff --git a/tests/__snapshots__/typegenPrinter.spec.ts.snap b/tests/__snapshots__/typegenPrinter.spec.ts.snap index 83bf1945..fd12064d 100644 --- a/tests/__snapshots__/typegenPrinter.spec.ts.snap +++ b/tests/__snapshots__/typegenPrinter.spec.ts.snap @@ -148,7 +148,8 @@ exports[`typegenPrinter should print the full output 1`] = ` * Do not make changes to this file directly */ -import * as t from \\"./__helpers/index\\" + +import { TestContext } from \\"./tests/__helpers/index\\" import { core } from \\"@nexus/schema\\" @@ -340,7 +341,7 @@ export type NexusGenFeaturesConfig = { } export interface NexusGenTypes { - context: t.TestContext; + context: TestContext; inputTypes: NexusGenInputs; rootTypes: NexusGenRootTypes; inputTypeShapes: NexusGenInputs & NexusGenEnums & NexusGenScalars; diff --git a/tests/backingTypes.spec.ts b/tests/backingTypes.spec.ts index 5fea2c8d..1eb6ce1c 100644 --- a/tests/backingTypes.spec.ts +++ b/tests/backingTypes.spec.ts @@ -59,7 +59,10 @@ describe('backingTypes', () => { source: path.join(__dirname, '_types.ts'), }, ], - contextType: 't.TestContext', + }, + contextType: { + module: path.join(__dirname, '_types.ts'), + export: 'TestContext', }, }) }) @@ -69,7 +72,7 @@ describe('backingTypes', () => { const typegenInfo = await metadata.getTypegenInfo(schema) const typegen = new TypegenPrinter(schema, { ...typegenInfo, - typegenFile: '', + typegenPath: '', }) expect(typegen.printEnumTypeMap()).toMatchSnapshot() @@ -80,7 +83,7 @@ describe('backingTypes', () => { const typegenInfo = await metadata.getTypegenInfo(schema) const typegen = new TypegenPrinter(schema, { ...typegenInfo, - typegenFile: '', + typegenPath: '', }) expect(typegen.printEnumTypeMap()).toMatchSnapshot() @@ -98,8 +101,8 @@ describe('rootTypings', () => { name: 'TestEnumType', members: TestEnum, rootTyping: { - path: __filename, - name: 'TestEnum', + module: __filename, + export: 'TestEnum', }, }), ], @@ -108,7 +111,7 @@ describe('rootTypings', () => { const typegenInfo = await metadata.getTypegenInfo(schema) const typegen = new TypegenPrinter(schema, { ...typegenInfo, - typegenFile: '', + typegenPath: '', }) expect(typegen.print()).toMatchSnapshot() }) @@ -120,8 +123,8 @@ describe('rootTypings', () => { const someType = objectType({ name: 'SomeType', rootTyping: { - name: 'invalid', - path: './fzeffezpokm', + export: 'invalid', + module: './fzeffezpokm', }, definition(t) { t.id('id') @@ -136,7 +139,7 @@ describe('rootTypings', () => { const typegenInfo = await metadata.getTypegenInfo(schema) const typegen = new TypegenPrinter(schema, { ...typegenInfo, - typegenFile: '', + typegenPath: '', }) expect(() => typegen.print()).toThrowErrorMatchingInlineSnapshot( @@ -151,8 +154,8 @@ describe('rootTypings', () => { const someType = objectType({ name: 'SomeType', rootTyping: { - name: 'invalid', - path: __dirname + '/invalid_path.ts', + export: 'invalid', + module: __dirname + '/invalid_path.ts', }, definition(t) { t.id('id') @@ -167,7 +170,7 @@ describe('rootTypings', () => { const typegenInfo = await metadata.getTypegenInfo(schema) const typegen = new TypegenPrinter(schema, { ...typegenInfo, - typegenFile: '', + typegenPath: '', }) try { diff --git a/tests/makeSchema.spec.ts b/tests/makeSchema.spec.ts index b82f699b..8fb080df 100644 --- a/tests/makeSchema.spec.ts +++ b/tests/makeSchema.spec.ts @@ -1,9 +1,18 @@ import { printSchema } from 'graphql' import os from 'os' import path from 'path' +import { objectType } from '../src' import { generateSchema, makeSchema } from '../src/builder' import { queryField } from '../src/definitions/queryField' +export type Test = { + id: string +} + +export type Context = { + foo: string +} + describe('makeSchema', () => { describe('shouldExitAfterGenerateArtifacts', () => { beforeEach(() => { @@ -94,12 +103,9 @@ describe('makeSchema', () => { }), ], shouldGenerateArtifacts: true, - typegenAutoConfig: { - sources: [], - contextType: { - path: 'graphql', - name: 'GraphQLInputFieldConfigMap', - }, + contextType: { + module: 'graphql', + export: 'GraphQLInputFieldConfigMap', }, }, path.normalize(`/dev/null/file.ts`) @@ -107,5 +113,42 @@ describe('makeSchema', () => { expect(tsTypes).toContain(`import { GraphQLInputFieldConfigMap } from "graphql"`) expect(tsTypes).toContain(`context: GraphQLInputFieldConfigMap`) }) + + it('does not clash with sources', async () => { + const { tsTypes } = await generateSchema.withArtifacts( + { + outputs: false, + types: [ + queryField('ok', { + description: 'Example boolean field', + type: 'Boolean', + }), + objectType({ + name: 'Test', + definition(t) { + t.id('id') + }, + }), + ], + shouldGenerateArtifacts: true, + typegenAutoConfig: { + sources: [ + { + source: __filename, + alias: 'thisFile', + }, + ], + }, + contextType: { + module: __filename, + export: 'Context', + }, + }, + path.join(__dirname, 'nexus.ts') + ) + + expect(tsTypes).toContain(`import * as thisFile from "./makeSchema.spec"`) + expect(tsTypes).toContain(`import { Context } from "./makeSchema.spec"`) + }) }) }) diff --git a/tests/scalarType.spec.ts b/tests/scalarType.spec.ts index 2162192c..921b252f 100644 --- a/tests/scalarType.spec.ts +++ b/tests/scalarType.spec.ts @@ -77,8 +77,8 @@ describe('scalarType', () => { scalarType({ name: 'TestScalar', rootTyping: { - path: 'graphql', - name: 'GraphQLScalar', + module: 'graphql', + export: 'GraphQLScalar', }, serialize() {}, }), diff --git a/tests/typegenPrinter.spec.ts b/tests/typegenPrinter.spec.ts index 9a70339f..0a403005 100644 --- a/tests/typegenPrinter.spec.ts +++ b/tests/typegenPrinter.spec.ts @@ -48,7 +48,10 @@ describe('typegenPrinter', () => { source: path.join(__dirname, '__helpers/index.ts'), }, ], - contextType: 't.TestContext', + }, + contextType: { + module: path.join(__dirname, '__helpers/index.ts'), + export: 'TestContext', }, }) @@ -62,7 +65,7 @@ describe('typegenPrinter', () => { const typegenInfo = await metadata.getTypegenInfo(schema) typegen = new TypegenPrinter(metadata.sortSchema(schema), { ...typegenInfo, - typegenFile: '', + typegenPath: '', }) jest .spyOn(typegen, 'hasResolver')