Skip to content

Commit

Permalink
feat(generator): ability to control import extension (#1284)
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonkuhrt authored Dec 18, 2024
1 parent d19d399 commit 0858fde
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 22 deletions.
22 changes: 22 additions & 0 deletions src/generator/config/config.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,28 @@
import { expect } from 'vitest'
import { describe } from 'vitest'
import { test } from '../../../tests/_/helpers.js'
import { createConfig } from './config.js'
import type { ConfigInitSchemaSdl } from './configInit.js'

const schema: ConfigInitSchemaSdl = {
type: `sdl`,
sdl: `type Query { ok: Boolean }`,
}

describe(`import format`, () => {
test(`defaults to jsExtension`, async () => {
const config = await createConfig({ schema })
expect(config.importFormat).toEqual(`jsExtension`)
})
test(`noExtension`, async () => {
const customPathFile = `./tests/_/fixtures/custom.graphql`
const config = await createConfig({
schema: { type: `sdlFile`, dirOrFilePath: customPathFile },
importFormat: `noExtension`,
})
expect(config.importFormat).toEqual(`noExtension`)
})
})

test(`can load schema from custom path`, async () => {
const customPathFile = `./tests/_/fixtures/custom.graphql`
Expand Down
5 changes: 4 additions & 1 deletion src/generator/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ import type { Extension } from '../extension/types.js'
import {
type ConfigInit,
type ConfigInitLibraryPaths,
type InputImportFormat,
type InputLint,
type InputOutputCase,
libraryPathKeys,
} from './configInit.js'
import { defaultLibraryPaths, defaultNamespace, defaultOutputCase } from './defaults.js'
import { defaultImportFormat, defaultLibraryPaths, defaultNamespace, defaultOutputCase } from './defaults.js'
import { defaultName } from './defaults.js'

export interface Config {
Expand Down Expand Up @@ -43,6 +44,7 @@ export interface Config {
}
formatter: Formatter
extensions: Extension[]
importFormat: InputImportFormat
paths: {
project: {
inputs: {
Expand Down Expand Up @@ -201,6 +203,7 @@ To suppress this warning disable formatting in one of the following ways:
return {
fs,
name,
importFormat: configInit.importFormat ?? defaultImportFormat,
nameNamespace,
extensions: configInit.extensions ?? [],
outputCase,
Expand Down
74 changes: 56 additions & 18 deletions src/generator/config/configInit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,39 @@ export const OutputCase = {
} as const
export type InputOutputCase = keyof typeof OutputCase

export const ImportFormat = {
jsExtension: `jsExtension`,
tsExtension: `tsExtension`,
noExtension: `noExtension`,
} as const
export type InputImportFormat = keyof typeof ImportFormat

export interface ConfigInitSchemaSdl {
type: `sdl`
sdl: string
}
export interface ConfigInitSchemaInstance {
type: `instance`
instance: Grafaid.Schema.Schema
}
export interface ConfigInitSchemaSdlFile {
type: `sdlFile`
/**
* Defaults to the source directory if set, otherwise the current working directory.
*/
dirOrFilePath?: string
}
export interface ConfigInitSchemaUrl {
type: `url`
url: URL
options?: InputIntrospectionOptions
}

export type ConfigInitSchema =
| ConfigInitSchemaSdl
| ConfigInitSchemaInstance
| ConfigInitSchemaSdlFile
| ConfigInitSchemaUrl
export interface ConfigInit {
/**
* File system API to use.
Expand Down Expand Up @@ -66,25 +99,13 @@ export interface ConfigInit {
*/
currentWorkingDirectory?: string
/**
* The schema to use for generation. Can be an existing SDL file on disk, a schema instance already in memory, or an endpoint that will be introspected.
* The schema to use for generation. Can be one of:
*
* 1. An existing SDL file on disk,
* 2. A schema instance already in memory,
* 3. An endpoint that will be introspected.
*/
schema: {
type: 'sdl'
sdl: string
} | {
type: 'instance'
instance: Grafaid.Schema.Schema
} | {
type: 'sdlFile'
/**
* Defaults to the source directory if set, otherwise the current working directory.
*/
dirOrFilePath?: string
} | {
type: 'url'
url: URL
options?: InputIntrospectionOptions
}
schema: ConfigInitSchema
/**
* If the schema comes from a non-sdl-file source like a GraphQL endpoint URL, should a derived SDL file be written to disk?
*
Expand Down Expand Up @@ -136,6 +157,23 @@ export interface ConfigInit {
* If not set, Graffle will look for a file called `scalars.ts` in the project directory.
*/
scalars?: string
/**
* How should import identifiers be generated? Can be one of:
*
* 1. `jsExtension` e.g. `import ... from './bar.js'`
* 2. `tsExtension` e.g. `import ... from './bar.ts'`
* 3. `noExtension` e.g. `import ... from './bar'`
*
* @defaultValue `jsExtension`
*
* @remarks
*
* A user request for this option can be found at https://github.com/graffle-js/graffle/issues/1282.
*
* There is a planned feature to have a default be dynamic according to the state of your project's tsconfig.json.
* See https://github.com/graffle-js/graffle/issues/1283.
*/
importFormat?: InputImportFormat
/**
* Override import paths to graffle package within the generated code.
* Used by Graffle test suite to have generated clients point to source
Expand Down
4 changes: 3 additions & 1 deletion src/generator/config/defaults.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ConfigInitLibraryPaths, InputOutputCase } from './configInit.js'
import type { ConfigInitLibraryPaths, InputImportFormat, InputOutputCase } from './configInit.js'

export const defaultNamespace = `Graffle`

Expand All @@ -13,3 +13,5 @@ export const defaultLibraryPaths = {
} satisfies ConfigInitLibraryPaths

export const defaultOutputCase: InputOutputCase = `kebab`

export const defaultImportFormat: InputImportFormat = `jsExtension`
28 changes: 27 additions & 1 deletion src/generator/generator/generate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,35 @@ import { globby } from 'globby'
import * as Memfs from 'memfs'
import { readFile } from 'node:fs/promises'
import * as Path from 'node:path'
import { expect, test } from 'vitest'
import { describe, expect, test } from 'vitest'
import type { ConfigInitSchemaSdl } from '../_.js'
import { generate } from './generate.js'

const schema: ConfigInitSchemaSdl = {
type: `sdl`,
sdl: `type Query { ok: Boolean }`,
}

describe(`importFormat`, () => {
test(`default is jsExtension`, async () => {
await generate({
fs: Memfs.fs.promises as any,
schema,
})
const SchemaTs = Memfs.fs.readFileSync(`./graffle/modules/schema.ts`, `utf8`)
expect(SchemaTs).toMatch(/import.*".\/data.js"/)
})
test(`noExtension`, async () => {
await generate({
fs: Memfs.fs.promises as any,
schema,
importFormat: `noExtension`,
})
const SchemaTs = Memfs.fs.readFileSync(`./graffle/modules/schema.ts`, `utf8`)
expect(SchemaTs).toMatch(/import.*".\/data"/)
})
})

test(`kitchen-sink generated modules`, async () => {
const basePath = `./tests/_/schemas/kitchen-sink/graffle`
const filePaths = await globby(`${basePath}/**/*.ts`)
Expand Down
12 changes: 11 additions & 1 deletion src/generator/helpers/moduleGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { camelCase, kebabCase, pascalCase, snakeCase } from 'es-toolkit'
import { casesExhausted } from '../../lib/prelude.js'
import type { Config } from '../config/config.js'
import {
createCodeGenerator,
Expand Down Expand Up @@ -61,7 +62,16 @@ export const getFileName = (config: Config, generator: ModuleGenerator | Generat

export const getImportName = (config: Config, generator: ModuleGenerator | GeneratedModule) => {
const name = getBaseName(config, generator)
return `${name}.js`
switch (config.importFormat) {
case `jsExtension`:
return `${name}.js`
case `tsExtension`:
return `${name}.ts`
case `noExtension`:
return name
default:
throw casesExhausted(config.importFormat)
}
}

export const caseFormatters = {
Expand Down

0 comments on commit 0858fde

Please sign in to comment.