From 2cbcbb37101708705ab94cec747997c8063f698d Mon Sep 17 00:00:00 2001 From: Saihajpreet Singh Date: Thu, 14 Jul 2022 12:35:03 -0400 Subject: [PATCH] feat: add `emitLegacyCommonJSImports` flag (#8077) * feat: add `emitLegacyCommonJSImports` flag * add changeset * update docs * fix * fix(emitLegacyCommonJSImports): normalize value across plugins and presets + fix specs Introduced a `shouldEmitLegacyCommonJSImports()` helper to get proper value Co-authored-by: Charly POLY --- .changeset/kind-taxis-refuse.md | 22 +++++++ dev-test/codegen.yml | 1 + .../comment.query.stencil-component.tsx | 2 +- .../feed-entry.fragment.stencil-component.tsx | 4 +- .../feed.query.stencil-component.tsx | 2 +- ...mit-comment.mutation.stencil-component.tsx | 2 +- dev-test/gql-tag-operations-urql/gql/gql.d.ts | 6 +- .../__generated__/CreateReviewForEpisode.tsx | 2 +- .../__generated__/HeroAndFriendsNames.tsx | 2 +- .../star-wars/__generated__/HeroAppearsIn.tsx | 2 +- .../star-wars/__generated__/HeroDetails.tsx | 2 +- .../__generated__/HeroDetailsFragment.tsx | 2 +- .../__generated__/HeroDetailsWithFragment.tsx | 4 +- dev-test/star-wars/__generated__/HeroName.tsx | 2 +- .../__generated__/HeroNameConditional.tsx | 2 +- .../HeroParentTypeDependentField.tsx | 2 +- .../HeroTypeDependentAliasedField.tsx | 2 +- .../star-wars/__generated__/HumanFields.tsx | 2 +- .../__generated__/HumanWithNullWeight.tsx | 4 +- .../star-wars/__generated__/TwoHeroes.tsx | 2 +- packages/graphql-codegen-cli/src/codegen.ts | 8 ++- packages/graphql-codegen-cli/src/config.ts | 22 +++++++ .../tests/cli-flags.spec.ts | 28 ++++++++ .../visitor-plugin-common/src/imports.ts | 8 ++- .../gql-tag-operations/src/index.ts | 19 ++++-- .../tests/gql-tag-operations.spec.ts | 10 +-- .../src/fragment-resolver.ts | 1 + .../src/resolve-document-imports.ts | 1 + .../tests/near-operation-file.spec.ts | 66 +++++++++++-------- packages/utils/plugins-helpers/src/types.ts | 4 ++ .../docs/config-reference/codegen-config.mdx | 4 +- 31 files changed, 173 insertions(+), 67 deletions(-) create mode 100644 .changeset/kind-taxis-refuse.md diff --git a/.changeset/kind-taxis-refuse.md b/.changeset/kind-taxis-refuse.md new file mode 100644 index 00000000000..4ff3bc4d87e --- /dev/null +++ b/.changeset/kind-taxis-refuse.md @@ -0,0 +1,22 @@ +--- +'@graphql-codegen/cli': minor +'@graphql-codegen/visitor-plugin-common': minor +'@graphql-codegen/gql-tag-operations': minor +'@graphql-codegen/near-operation-file-preset': minor +'@graphql-codegen/plugin-helpers': minor +--- + +Add new flag to emit legacy common js imports. Default it will be `true` this way it ensure that generated code works with [non-compliant bundlers](https://github.com/dotansimha/graphql-code-generator/issues/8065). + +You can use the option in your config: +```yaml +schema: 'schema.graphql' + documents: + - 'src/**/*.graphql' + emitLegacyCommonJSImports: true +``` + +Alternative you can use the CLI to set this option: +```bash + $ codegen --config-file=config.yml --emit-legacy-common-js-imports +``` diff --git a/dev-test/codegen.yml b/dev-test/codegen.yml index 2f10959c049..2902df2a5e4 100644 --- a/dev-test/codegen.yml +++ b/dev-test/codegen.yml @@ -1,6 +1,7 @@ hooks: afterAllFileWrite: - prettier --write +emitLegacyCommonJSImports: false generates: ./dev-test/test-schema/resolvers-types.ts: schema: ./dev-test/test-schema/schema-text.js diff --git a/dev-test/githunt/__generated__/comment.query.stencil-component.tsx b/dev-test/githunt/__generated__/comment.query.stencil-component.tsx index f7c18491a01..8dfd4790524 100644 --- a/dev-test/githunt/__generated__/comment.query.stencil-component.tsx +++ b/dev-test/githunt/__generated__/comment.query.stencil-component.tsx @@ -1,5 +1,5 @@ import gql from 'graphql-tag'; -import { CommentsPageCommentFragmentDoc } from './comments-page-comment.fragment.stencil-component'; +import { CommentsPageCommentFragmentDoc } from './comments-page-comment.fragment.stencil-component.js'; import 'stencil-apollo'; import { Component, Prop, h } from '@stencil/core'; diff --git a/dev-test/githunt/__generated__/feed-entry.fragment.stencil-component.tsx b/dev-test/githunt/__generated__/feed-entry.fragment.stencil-component.tsx index 03b3578a297..4a0db27deb1 100644 --- a/dev-test/githunt/__generated__/feed-entry.fragment.stencil-component.tsx +++ b/dev-test/githunt/__generated__/feed-entry.fragment.stencil-component.tsx @@ -1,6 +1,6 @@ import gql from 'graphql-tag'; -import { VoteButtonsFragmentDoc } from './vote-buttons.fragment.stencil-component'; -import { RepoInfoFragmentDoc } from './repo-info.fragment.stencil-component'; +import { VoteButtonsFragmentDoc } from './vote-buttons.fragment.stencil-component.js'; +import { RepoInfoFragmentDoc } from './repo-info.fragment.stencil-component.js'; declare global { export type FeedEntryFragment = { diff --git a/dev-test/githunt/__generated__/feed.query.stencil-component.tsx b/dev-test/githunt/__generated__/feed.query.stencil-component.tsx index 38fe936892a..a00ace93de0 100644 --- a/dev-test/githunt/__generated__/feed.query.stencil-component.tsx +++ b/dev-test/githunt/__generated__/feed.query.stencil-component.tsx @@ -1,5 +1,5 @@ import gql from 'graphql-tag'; -import { FeedEntryFragmentDoc } from './feed-entry.fragment.stencil-component'; +import { FeedEntryFragmentDoc } from './feed-entry.fragment.stencil-component.js'; import 'stencil-apollo'; import { Component, Prop, h } from '@stencil/core'; diff --git a/dev-test/githunt/__generated__/submit-comment.mutation.stencil-component.tsx b/dev-test/githunt/__generated__/submit-comment.mutation.stencil-component.tsx index 7d4c297ab84..0978964508a 100644 --- a/dev-test/githunt/__generated__/submit-comment.mutation.stencil-component.tsx +++ b/dev-test/githunt/__generated__/submit-comment.mutation.stencil-component.tsx @@ -1,5 +1,5 @@ import gql from 'graphql-tag'; -import { CommentsPageCommentFragmentDoc } from './comments-page-comment.fragment.stencil-component'; +import { CommentsPageCommentFragmentDoc } from './comments-page-comment.fragment.stencil-component.js'; import 'stencil-apollo'; import { Component, Prop, h } from '@stencil/core'; diff --git a/dev-test/gql-tag-operations-urql/gql/gql.d.ts b/dev-test/gql-tag-operations-urql/gql/gql.d.ts index 2bb13e0433b..1ca307e0d36 100644 --- a/dev-test/gql-tag-operations-urql/gql/gql.d.ts +++ b/dev-test/gql-tag-operations-urql/gql/gql.d.ts @@ -4,13 +4,13 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/ declare module '@urql/core' { export function gql( source: '\n query Foo {\n Tweets {\n id\n }\n }\n' - ): typeof import('./graphql').FooDocument; + ): typeof import('./graphql.js').FooDocument; export function gql( source: '\n fragment Lel on Tweet {\n id\n body\n }\n' - ): typeof import('./graphql').LelFragmentDoc; + ): typeof import('./graphql.js').LelFragmentDoc; export function gql( source: '\n query Bar {\n Tweets {\n ...Lel\n }\n }\n' - ): typeof import('./graphql').BarDocument; + ): typeof import('./graphql.js').BarDocument; export function gql(source: string): unknown; export type DocumentType> = TDocumentNode extends DocumentNode< diff --git a/dev-test/star-wars/__generated__/CreateReviewForEpisode.tsx b/dev-test/star-wars/__generated__/CreateReviewForEpisode.tsx index ec5bc7e3e7b..fd3e7b16b03 100644 --- a/dev-test/star-wars/__generated__/CreateReviewForEpisode.tsx +++ b/dev-test/star-wars/__generated__/CreateReviewForEpisode.tsx @@ -1,4 +1,4 @@ -import * as Types from '../types.d'; +import * as Types from '../types.d.js'; import { gql } from '@apollo/client'; import * as Apollo from '@apollo/client'; diff --git a/dev-test/star-wars/__generated__/HeroAndFriendsNames.tsx b/dev-test/star-wars/__generated__/HeroAndFriendsNames.tsx index 2f9b2a42ec7..add0662e4ac 100644 --- a/dev-test/star-wars/__generated__/HeroAndFriendsNames.tsx +++ b/dev-test/star-wars/__generated__/HeroAndFriendsNames.tsx @@ -1,4 +1,4 @@ -import * as Types from '../types.d'; +import * as Types from '../types.d.js'; import { gql } from '@apollo/client'; import * as Apollo from '@apollo/client'; diff --git a/dev-test/star-wars/__generated__/HeroAppearsIn.tsx b/dev-test/star-wars/__generated__/HeroAppearsIn.tsx index b3af39ffd22..36686f0a394 100644 --- a/dev-test/star-wars/__generated__/HeroAppearsIn.tsx +++ b/dev-test/star-wars/__generated__/HeroAppearsIn.tsx @@ -1,4 +1,4 @@ -import * as Types from '../types.d'; +import * as Types from '../types.d.js'; import { gql } from '@apollo/client'; import * as Apollo from '@apollo/client'; diff --git a/dev-test/star-wars/__generated__/HeroDetails.tsx b/dev-test/star-wars/__generated__/HeroDetails.tsx index f555e4e4c40..74ab5dbcbb1 100644 --- a/dev-test/star-wars/__generated__/HeroDetails.tsx +++ b/dev-test/star-wars/__generated__/HeroDetails.tsx @@ -1,4 +1,4 @@ -import * as Types from '../types.d'; +import * as Types from '../types.d.js'; import { gql } from '@apollo/client'; import * as Apollo from '@apollo/client'; diff --git a/dev-test/star-wars/__generated__/HeroDetailsFragment.tsx b/dev-test/star-wars/__generated__/HeroDetailsFragment.tsx index 339e65210ed..2ad8d109fd7 100644 --- a/dev-test/star-wars/__generated__/HeroDetailsFragment.tsx +++ b/dev-test/star-wars/__generated__/HeroDetailsFragment.tsx @@ -1,4 +1,4 @@ -import * as Types from '../types.d'; +import * as Types from '../types.d.js'; import { gql } from '@apollo/client'; export type HeroDetails_Droid_Fragment = { __typename?: 'Droid'; primaryFunction?: string | null; name: string }; diff --git a/dev-test/star-wars/__generated__/HeroDetailsWithFragment.tsx b/dev-test/star-wars/__generated__/HeroDetailsWithFragment.tsx index b9deb103c77..3a92c091c09 100644 --- a/dev-test/star-wars/__generated__/HeroDetailsWithFragment.tsx +++ b/dev-test/star-wars/__generated__/HeroDetailsWithFragment.tsx @@ -1,7 +1,7 @@ -import * as Types from '../types.d'; +import * as Types from '../types.d.js'; import { gql } from '@apollo/client'; -import { HeroDetailsFragmentDoc } from './HeroDetailsFragment'; +import { HeroDetailsFragmentDoc } from './HeroDetailsFragment.js'; import * as Apollo from '@apollo/client'; const defaultOptions = {} as const; export type HeroDetailsWithFragmentQueryVariables = Types.Exact<{ diff --git a/dev-test/star-wars/__generated__/HeroName.tsx b/dev-test/star-wars/__generated__/HeroName.tsx index 1dbc7d0c595..94083ad5fbc 100644 --- a/dev-test/star-wars/__generated__/HeroName.tsx +++ b/dev-test/star-wars/__generated__/HeroName.tsx @@ -1,4 +1,4 @@ -import * as Types from '../types.d'; +import * as Types from '../types.d.js'; import { gql } from '@apollo/client'; import * as Apollo from '@apollo/client'; diff --git a/dev-test/star-wars/__generated__/HeroNameConditional.tsx b/dev-test/star-wars/__generated__/HeroNameConditional.tsx index 82d92f2859a..253175dc910 100644 --- a/dev-test/star-wars/__generated__/HeroNameConditional.tsx +++ b/dev-test/star-wars/__generated__/HeroNameConditional.tsx @@ -1,4 +1,4 @@ -import * as Types from '../types.d'; +import * as Types from '../types.d.js'; import { gql } from '@apollo/client'; import * as Apollo from '@apollo/client'; diff --git a/dev-test/star-wars/__generated__/HeroParentTypeDependentField.tsx b/dev-test/star-wars/__generated__/HeroParentTypeDependentField.tsx index f8e9d6dd719..6b138a03b6c 100644 --- a/dev-test/star-wars/__generated__/HeroParentTypeDependentField.tsx +++ b/dev-test/star-wars/__generated__/HeroParentTypeDependentField.tsx @@ -1,4 +1,4 @@ -import * as Types from '../types.d'; +import * as Types from '../types.d.js'; import { gql } from '@apollo/client'; import * as Apollo from '@apollo/client'; diff --git a/dev-test/star-wars/__generated__/HeroTypeDependentAliasedField.tsx b/dev-test/star-wars/__generated__/HeroTypeDependentAliasedField.tsx index 394cdb252e0..9dfa404fe5f 100644 --- a/dev-test/star-wars/__generated__/HeroTypeDependentAliasedField.tsx +++ b/dev-test/star-wars/__generated__/HeroTypeDependentAliasedField.tsx @@ -1,4 +1,4 @@ -import * as Types from '../types.d'; +import * as Types from '../types.d.js'; import { gql } from '@apollo/client'; import * as Apollo from '@apollo/client'; diff --git a/dev-test/star-wars/__generated__/HumanFields.tsx b/dev-test/star-wars/__generated__/HumanFields.tsx index 100b1130142..65ae593bac1 100644 --- a/dev-test/star-wars/__generated__/HumanFields.tsx +++ b/dev-test/star-wars/__generated__/HumanFields.tsx @@ -1,4 +1,4 @@ -import * as Types from '../types.d'; +import * as Types from '../types.d.js'; import { gql } from '@apollo/client'; export type HumanFieldsFragment = { __typename?: 'Human'; name: string; mass?: number | null }; diff --git a/dev-test/star-wars/__generated__/HumanWithNullWeight.tsx b/dev-test/star-wars/__generated__/HumanWithNullWeight.tsx index dc9825d0332..469173dc2fb 100644 --- a/dev-test/star-wars/__generated__/HumanWithNullWeight.tsx +++ b/dev-test/star-wars/__generated__/HumanWithNullWeight.tsx @@ -1,7 +1,7 @@ -import * as Types from '../types.d'; +import * as Types from '../types.d.js'; import { gql } from '@apollo/client'; -import { HumanFieldsFragmentDoc } from './HumanFields'; +import { HumanFieldsFragmentDoc } from './HumanFields.js'; import * as Apollo from '@apollo/client'; const defaultOptions = {} as const; export type HumanWithNullHeightQueryVariables = Types.Exact<{ [key: string]: never }>; diff --git a/dev-test/star-wars/__generated__/TwoHeroes.tsx b/dev-test/star-wars/__generated__/TwoHeroes.tsx index ee7e39e6a6a..1165b2c71a9 100644 --- a/dev-test/star-wars/__generated__/TwoHeroes.tsx +++ b/dev-test/star-wars/__generated__/TwoHeroes.tsx @@ -1,4 +1,4 @@ -import * as Types from '../types.d'; +import * as Types from '../types.d.js'; import { gql } from '@apollo/client'; import * as Apollo from '@apollo/client'; diff --git a/packages/graphql-codegen-cli/src/codegen.ts b/packages/graphql-codegen-cli/src/codegen.ts index eb64f18ad97..75b4f5d55bd 100644 --- a/packages/graphql-codegen-cli/src/codegen.ts +++ b/packages/graphql-codegen-cli/src/codegen.ts @@ -16,7 +16,7 @@ import { GraphQLError, GraphQLSchema, DocumentNode } from 'graphql'; import { getPluginByName } from './plugins.js'; import { getPresetByName } from './presets.js'; import { debugLog } from './utils/debugging.js'; -import { CodegenContext, ensureContext } from './config.js'; +import { CodegenContext, ensureContext, shouldEmitLegacyCommonJSImports } from './config.js'; import fs from 'fs'; import path from 'path'; import { cpus } from 'os'; @@ -315,6 +315,7 @@ export async function executeCodegen(input: CodegenContext | Types.Config): Prom ...(typeof outputFileTemplateConfig === 'string' ? { value: outputFileTemplateConfig } : outputFileTemplateConfig), + emitLegacyCommonJSImports: shouldEmitLegacyCommonJSImports(config, filename), }; const outputs: Types.GenerateOptions[] = hasPreset @@ -350,7 +351,10 @@ export async function executeCodegen(input: CodegenContext | Types.Config): Prom const process = async (outputArgs: Types.GenerateOptions) => { const output = await codegen({ - ...outputArgs, + ...{ + ...outputArgs, + emitLegacyCommonJSImports: shouldEmitLegacyCommonJSImports(config, outputArgs.filename), + }, cache, }); result.push({ diff --git a/packages/graphql-codegen-cli/src/config.ts b/packages/graphql-codegen-cli/src/config.ts index 7d2cebf92c1..c084862c687 100644 --- a/packages/graphql-codegen-cli/src/config.ts +++ b/packages/graphql-codegen-cli/src/config.ts @@ -31,6 +31,7 @@ export type YamlCliFlags = { errorsOnly: boolean; profile: boolean; ignoreNoDocuments?: boolean; + emitLegacyCommonJSImports?: boolean; }; export function generateSearchPlaces(moduleName: string) { @@ -287,6 +288,11 @@ export function updateContextWithCliFlags(context: CodegenContext, cliFlags: Yam config.ignoreNoDocuments = cliFlags['ignore-no-documents'] === true; } + if (cliFlags['emit-legacy-common-js-imports'] !== undefined) { + // for some reason parsed value is `'false'` string so this ensure it always is a boolean. + config.emitLegacyCommonJSImports = cliFlags['emit-legacy-common-js-imports'] === true; + } + if (cliFlags.project) { context.useProject(cliFlags.project); } @@ -439,3 +445,19 @@ function addHashToDocumentFiles(documentFilesPromise: Promise { expect(config.ignoreNoDocuments).toBeFalsy(); }); + it('Should overwrite emitLegacyCommonJSImports config using cli flags to true', async () => { + mockConfig(` + schema: schema.graphql + emitLegacyCommonJSImports: false + generates: + file.ts: + - plugin + `); + const args = createArgv('--emit-legacy-common-js-imports'); + const context = await createContext(parseArgv(args)); + const config = context.getConfig(); + expect(config.emitLegacyCommonJSImports).toBeTruthy(); + }); + + it('Should overwrite emitLegacyCommonJSImports config using cli flags to false', async () => { + mockConfig(` + schema: schema.graphql + emitLegacyCommonJSImports: true + generates: + file.ts: + - plugin + `); + const args = createArgv('--emit-legacy-common-js-imports=false'); + const context = await createContext(parseArgv(args)); + const config = context.getConfig(); + expect(config.emitLegacyCommonJSImports).toBeFalsy(); + }); + it('Should overwrite ignoreNoDocuments config using cli flags to true', async () => { mockConfig(` schema: schema.graphql diff --git a/packages/plugins/other/visitor-plugin-common/src/imports.ts b/packages/plugins/other/visitor-plugin-common/src/imports.ts index d82f4c61857..df2eb944bf2 100644 --- a/packages/plugins/other/visitor-plugin-common/src/imports.ts +++ b/packages/plugins/other/visitor-plugin-common/src/imports.ts @@ -7,6 +7,7 @@ export type ImportDeclaration = { baseOutputDir: string; baseDir: string; typesImport: boolean; + emitLegacyCommonJSImports: boolean; }; export type ImportSource = { @@ -56,9 +57,14 @@ export function generateImportStatement(statement: ImportDeclaration): string { importSource.identifiers && importSource.identifiers.length ? `{ ${Array.from(new Set(importSource.identifiers)).join(', ')} }` : '*'; + const importExtension = + importPath.startsWith('/') || importPath.startsWith('.') ? (statement.emitLegacyCommonJSImports ? '' : '.js') : ''; const importAlias = importSource.namespace ? ` as ${importSource.namespace}` : ''; const importStatement = typesImport ? 'import type' : 'import'; - return `${importStatement} ${importNames}${importAlias} from '${importPath}';${importAlias ? '\n' : ''}`; + return `${importStatement} ${importNames}${importAlias} from '${importPath}${importExtension}';${ + importAlias ? '\n' : '' + }`; + // return `${importStatement} ${importNames}${importAlias} from '${importPath}';${importAlias ? '\n' : ''}`; } function resolveImportPath(baseDir: string, outputPath: string, sourcePath: string) { diff --git a/packages/plugins/typescript/gql-tag-operations/src/index.ts b/packages/plugins/typescript/gql-tag-operations/src/index.ts index 07e4ffbc81e..2e5a8021f80 100644 --- a/packages/plugins/typescript/gql-tag-operations/src/index.ts +++ b/packages/plugins/typescript/gql-tag-operations/src/index.ts @@ -25,21 +25,22 @@ export const plugin: PluginFunction<{ sourcesWithOperations: Array; useTypeImports?: boolean; augmentedModuleName?: string; -}> = (_, __, { sourcesWithOperations, useTypeImports, augmentedModuleName }, _info) => { + emitLegacyCommonJSImports?: boolean; +}> = (_, __, { sourcesWithOperations, useTypeImports, augmentedModuleName, emitLegacyCommonJSImports }, _info) => { if (!sourcesWithOperations) { return ''; } if (augmentedModuleName == null) { return [ - `import * as graphql from './graphql.js';\n`, + `import * as graphql from './graphql${emitLegacyCommonJSImports ? '' : '.js'}';\n`, `${ useTypeImports ? 'import type' : 'import' } { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';\n`, `\n`, ...getDocumentRegistryChunk(sourcesWithOperations), `\n`, - ...getGqlOverloadChunk(sourcesWithOperations, 'lookup'), + ...getGqlOverloadChunk(sourcesWithOperations, 'lookup', emitLegacyCommonJSImports), `\n`, `export function gql(source: string): unknown;\n`, `export function gql(source: string) {\n`, @@ -55,7 +56,7 @@ export const plugin: PluginFunction<{ `declare module "${augmentedModuleName}" {`, [ `\n`, - ...getGqlOverloadChunk(sourcesWithOperations, 'augmented'), + ...getGqlOverloadChunk(sourcesWithOperations, 'augmented', emitLegacyCommonJSImports), `export function gql(source: string): unknown;\n`, `\n`, ...documentTypePartial, @@ -84,7 +85,11 @@ function getDocumentRegistryChunk(sourcesWithOperations: Array, mode: Mode) { +function getGqlOverloadChunk( + sourcesWithOperations: Array, + mode: Mode, + emitLegacyCommonJSImports?: boolean +) { const lines = new Set(); // We intentionally don't use a generic, because TS @@ -94,7 +99,9 @@ function getGqlOverloadChunk(sourcesWithOperations: Array, const returnType = mode === 'lookup' ? `(typeof documents)[${JSON.stringify(originalString)}]` - : `typeof import('./graphql').${operations[0].initialName}`; + : emitLegacyCommonJSImports + ? `typeof import('./graphql').${operations[0].initialName}` + : `typeof import('./graphql.js').${operations[0].initialName}`; lines.add(`export function gql(source: ${JSON.stringify(originalString)}): ${returnType};\n`); } diff --git a/packages/presets/gql-tag-operations/tests/gql-tag-operations.spec.ts b/packages/presets/gql-tag-operations/tests/gql-tag-operations.spec.ts index bee22be7488..20ee69bfbe2 100644 --- a/packages/presets/gql-tag-operations/tests/gql-tag-operations.spec.ts +++ b/packages/presets/gql-tag-operations/tests/gql-tag-operations.spec.ts @@ -36,7 +36,7 @@ describe('gql-tag-operations-preset', () => { const gqlFile = result.find(file => file.filename === 'out1/gql.ts'); expect(gqlFile.content).toMatchInlineSnapshot(` "/* eslint-disable */ - import * as graphql from './graphql.js'; + import * as graphql from './graphql'; import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; const documents = { @@ -91,7 +91,7 @@ describe('gql-tag-operations-preset', () => { const gqlFile = result.find(file => file.filename === 'out1/gql.ts'); expect(gqlFile.content).toMatchInlineSnapshot(` "/* eslint-disable */ - import * as graphql from './graphql.js'; + import * as graphql from './graphql'; import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; const documents = { @@ -139,7 +139,7 @@ describe('gql-tag-operations-preset', () => { const gqlFile = result.find(file => file.filename === 'out1/gql.ts'); expect(gqlFile.content).toMatchInlineSnapshot(` "/* eslint-disable */ - import * as graphql from './graphql.js'; + import * as graphql from './graphql'; import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; const documents = { @@ -188,7 +188,7 @@ describe('gql-tag-operations-preset', () => { const gqlFile = result.find(file => file.filename === 'out1/gql.ts'); expect(gqlFile.content).toMatchInlineSnapshot(` "/* eslint-disable */ - import * as graphql from './graphql.js'; + import * as graphql from './graphql'; import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; const documents = { @@ -283,7 +283,7 @@ describe('gql-tag-operations-preset', () => { const gqlFile = result.find(file => file.filename === 'out1/gql.ts'); expect(gqlFile.content).toMatchInlineSnapshot(` "/* eslint-disable */ - import * as graphql from './graphql.js'; + import * as graphql from './graphql'; import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; const documents = { diff --git a/packages/presets/near-operation-file/src/fragment-resolver.ts b/packages/presets/near-operation-file/src/fragment-resolver.ts index 68e2da08ee9..2927e1f0bbc 100644 --- a/packages/presets/near-operation-file/src/fragment-resolver.ts +++ b/packages/presets/near-operation-file/src/fragment-resolver.ts @@ -183,6 +183,7 @@ export default function buildFragmentResolver( path: fragmentsFilePath, identifiers, }, + emitLegacyCommonJSImports: presetOptions.config.emitLegacyCommonJSImports, typesImport, }) ), diff --git a/packages/presets/near-operation-file/src/resolve-document-imports.ts b/packages/presets/near-operation-file/src/resolve-document-imports.ts index b3367674de5..5fd46f5ab93 100644 --- a/packages/presets/near-operation-file/src/resolve-document-imports.ts +++ b/packages/presets/near-operation-file/src/resolve-document-imports.ts @@ -71,6 +71,7 @@ export function resolveDocumentImports( if (isUsingTypes(externalFragmentsInjectedDocument, [], schemaObject)) { const schemaTypesImportStatement = generateImportStatement({ baseDir, + emitLegacyCommonJSImports: presetOptions.config.emitLegacyCommonJSImports, importSource: resolveImportSource(schemaTypesSource), baseOutputDir, outputPath: generatedFilePath, diff --git a/packages/presets/near-operation-file/tests/near-operation-file.spec.ts b/packages/presets/near-operation-file/tests/near-operation-file.spec.ts index f7a0f2aee5d..4b122d3f9f5 100644 --- a/packages/presets/near-operation-file/tests/near-operation-file.spec.ts +++ b/packages/presets/near-operation-file/tests/near-operation-file.spec.ts @@ -6,6 +6,14 @@ import path from 'path'; import { preset } from '../src/index.js'; describe('near-operation-file preset', () => { + const executePreset: typeof preset.buildGeneratesSection = options => + preset.buildGeneratesSection({ + ...options, + config: { + ...options.config, + emitLegacyCommonJSImports: true, + }, + }); const schemaDocumentNode = parse(/* GraphQL */ ` type Query { user(id: String): User! @@ -149,7 +157,7 @@ describe('near-operation-file preset', () => { ]; expect(async () => { - await preset.buildGeneratesSection({ + await executePreset({ baseOutputDir: './src/', config: {}, presetConfig: { @@ -188,7 +196,7 @@ describe('near-operation-file preset', () => { number: Int! } `); - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: {}, presetConfig: { @@ -265,7 +273,7 @@ describe('near-operation-file preset', () => { }); it('#2365 - Should not add Fragment suffix to import identifier when dedupeOperationSuffix: true', async () => { - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: { dedupeOperationSuffix: true, @@ -321,7 +329,7 @@ describe('near-operation-file preset', () => { }); it('#2365 - Should add Fragment suffix to import identifier when dedupeOperationSuffix not set', async () => { - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: {}, presetConfig: { @@ -496,7 +504,7 @@ describe('near-operation-file preset', () => { } `); - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: { dedupeOperationSuffix: true, @@ -583,7 +591,7 @@ describe('near-operation-file preset', () => { it('should not add imports for fragments in the same location', async () => { const location = '/some/deep/path/src/graphql/me-query.graphql'; - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: { dedupeOperationSuffix: true, @@ -624,7 +632,7 @@ describe('near-operation-file preset', () => { }); it('Should build the correct operation files paths', async () => { - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: {}, presetConfig: { @@ -649,7 +657,7 @@ describe('near-operation-file preset', () => { }); it('Should build the correct operation files paths with a subfolder', async () => { - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: {}, presetConfig: { @@ -674,7 +682,7 @@ describe('near-operation-file preset', () => { }); it('Should skip the duplicate documents validation', async () => { - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: {}, presetConfig: { @@ -691,7 +699,7 @@ describe('near-operation-file preset', () => { }); it('Should allow to customize output extension', async () => { - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: {}, presetConfig: { @@ -718,7 +726,7 @@ describe('near-operation-file preset', () => { }); it('Should prepend the "add" plugin with the correct import', async () => { - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: {}, presetConfig: { @@ -744,7 +752,7 @@ describe('near-operation-file preset', () => { }); it('Should prepend the "add" plugin with the correct import when used with package name', async () => { - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: {}, presetConfig: { @@ -770,7 +778,7 @@ describe('near-operation-file preset', () => { }); it('Should prepend the "add" plugin with the correct import, when only using fragment spread', async () => { - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: {}, presetConfig: { @@ -800,7 +808,7 @@ describe('near-operation-file preset', () => { it('should fail when multiple fragments with the same name but different definition are found', () => { expect(() => - preset.buildGeneratesSection({ + executePreset({ baseOutputDir: './src/', config: {}, presetConfig: { @@ -828,7 +836,7 @@ describe('near-operation-file preset', () => { it('should NOT fail when multiple fragments with the same name and definition are found', () => { expect(() => - preset.buildGeneratesSection({ + executePreset({ baseOutputDir: './src/', config: {}, presetConfig: { @@ -845,7 +853,7 @@ describe('near-operation-file preset', () => { }); it('Should NOT prepend the "add" plugin with Types import when selection set does not include direct fields', async () => { - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: {}, presetConfig: { @@ -877,7 +885,7 @@ describe('near-operation-file preset', () => { }); it('Should prepend the "add" plugin with Types import when arguments are used', async () => { - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: {}, presetConfig: { @@ -915,7 +923,7 @@ describe('near-operation-file preset', () => { }); it('Should prepend the "add" plugin with the correct import (long path)', async () => { - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: {}, presetConfig: { @@ -946,7 +954,7 @@ describe('near-operation-file preset', () => { }); it('Should prepend the "add" plugin with the correct import (siblings)', async () => { - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: {}, presetConfig: { @@ -977,7 +985,7 @@ describe('near-operation-file preset', () => { }); it('Should not generate an absolute path if the path starts with "~"', async () => { - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: {}, presetConfig: { @@ -1008,7 +1016,7 @@ describe('near-operation-file preset', () => { }); it('Should add "add" plugin to plugins map if its not there', async () => { - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: {}, presetConfig: { @@ -1026,7 +1034,7 @@ describe('near-operation-file preset', () => { }); it('Should add "namespacedImportName" to config', async () => { - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: {}, presetConfig: { @@ -1044,7 +1052,7 @@ describe('near-operation-file preset', () => { }); it('Should add import to external fragment when its in use', async () => { - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: {}, presetConfig: { @@ -1078,7 +1086,7 @@ describe('near-operation-file preset', () => { it('Should allow external fragments to be imported from packages with function', async () => { const spy = jest.fn(); - await preset.buildGeneratesSection({ + await executePreset({ baseOutputDir: './src/', config: {}, presetConfig: { @@ -1099,7 +1107,7 @@ describe('near-operation-file preset', () => { }); it('Should allow external fragments to be imported from packages', async () => { - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: {}, presetConfig: { @@ -1133,7 +1141,7 @@ describe('near-operation-file preset', () => { }); it('Should add import to external fragment when its in use (long path)', async () => { - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: {}, presetConfig: { @@ -1159,7 +1167,7 @@ describe('near-operation-file preset', () => { }); it('Should add import to external fragment when its in use (nested fragment)', async () => { - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: {}, presetConfig: { @@ -1252,7 +1260,7 @@ describe('near-operation-file preset', () => { }, ]; - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: { skipTypename: true, @@ -1331,7 +1339,7 @@ describe('near-operation-file preset', () => { }, ]; - const result = await preset.buildGeneratesSection({ + const result = await executePreset({ baseOutputDir: './src/', config: { dedupeFragments: true, diff --git a/packages/utils/plugins-helpers/src/types.ts b/packages/utils/plugins-helpers/src/types.ts index 074413e684b..7080db4b0bd 100644 --- a/packages/utils/plugins-helpers/src/types.ts +++ b/packages/utils/plugins-helpers/src/types.ts @@ -437,6 +437,10 @@ export namespace Types { * @description A flag to suppress non-zero exit code when there are no documents to generate. */ ignoreNoDocuments?: boolean; + /** + * @description A flag to disable adding `.js` extension to the output file. Default: `true`. + */ + emitLegacyCommonJSImports?: boolean; /** * @description A flag to suppress printing errors when they occur. */ diff --git a/website/src/pages/docs/config-reference/codegen-config.mdx b/website/src/pages/docs/config-reference/codegen-config.mdx index 7bcfd01fda4..fe8d70a131d 100644 --- a/website/src/pages/docs/config-reference/codegen-config.mdx +++ b/website/src/pages/docs/config-reference/codegen-config.mdx @@ -69,7 +69,9 @@ Here are the supported options that you can define in the config file (see [sour - **`ignoreNoDocuments`** - A flag to not exit with non-zero exit code when there are no documents -- **`errorsOnly`** - A flag to suppress printing anything except errors +- **`emitLegacyCommonJSImports`** - A flag to emit imports without `.js` extension. Enabled by default. + +- **`errorsOnly`** - A flag to suppress printing anything except errors. - **`hooks`** - Specifies scripts to run when events are happening in the codegen's core. You can read more about lifecycle hooks [here](./lifecycle-hooks). You can specify this on your root configuration or on each output