Skip to content

Commit

Permalink
feat: add emitLegacyCommonJSImports flag (#8077)
Browse files Browse the repository at this point in the history
* 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 <cpoly55@gmail.com>
  • Loading branch information
saihaj and charlypoly committed Jul 14, 2022
1 parent 9e5773c commit 2cbcbb3
Show file tree
Hide file tree
Showing 31 changed files with 173 additions and 67 deletions.
22 changes: 22 additions & 0 deletions .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
```
1 change: 1 addition & 0 deletions 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
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions dev-test/gql-tag-operations-urql/gql/gql.d.ts
Expand Up @@ -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<any, any>> = TDocumentNode extends DocumentNode<
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dev-test/star-wars/__generated__/HeroAndFriendsNames.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dev-test/star-wars/__generated__/HeroAppearsIn.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dev-test/star-wars/__generated__/HeroDetails.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dev-test/star-wars/__generated__/HeroDetailsFragment.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions dev-test/star-wars/__generated__/HeroDetailsWithFragment.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dev-test/star-wars/__generated__/HeroName.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dev-test/star-wars/__generated__/HeroNameConditional.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dev-test/star-wars/__generated__/HumanFields.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions dev-test/star-wars/__generated__/HumanWithNullWeight.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dev-test/star-wars/__generated__/TwoHeroes.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions packages/graphql-codegen-cli/src/codegen.ts
Expand Up @@ -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';
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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({
Expand Down
22 changes: 22 additions & 0 deletions packages/graphql-codegen-cli/src/config.ts
Expand Up @@ -31,6 +31,7 @@ export type YamlCliFlags = {
errorsOnly: boolean;
profile: boolean;
ignoreNoDocuments?: boolean;
emitLegacyCommonJSImports?: boolean;
};

export function generateSearchPlaces(moduleName: string) {
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -439,3 +445,19 @@ function addHashToDocumentFiles(documentFilesPromise: Promise<Types.DocumentFile
})
);
}

export function shouldEmitLegacyCommonJSImports(config: Types.Config, outputPath: string): boolean {
const globalValue = config.emitLegacyCommonJSImports === undefined ? true : !!config.emitLegacyCommonJSImports;
// const outputConfig = config.generates[outputPath];

// if (!outputConfig) {
// debugLog(`Couldn't find a config of ${outputPath}`);
// return globalValue;
// }

// if (isConfiguredOutput(outputConfig) && typeof outputConfig.emitLegacyCommonJSImports === 'boolean') {
// return outputConfig.emitLegacyCommonJSImports;
// }

return globalValue;
}
28 changes: 28 additions & 0 deletions packages/graphql-codegen-cli/tests/cli-flags.spec.ts
Expand Up @@ -134,6 +134,34 @@ describe('CLI Flags', () => {
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
Expand Down
8 changes: 7 additions & 1 deletion packages/plugins/other/visitor-plugin-common/src/imports.ts
Expand Up @@ -7,6 +7,7 @@ export type ImportDeclaration<T = string> = {
baseOutputDir: string;
baseDir: string;
typesImport: boolean;
emitLegacyCommonJSImports: boolean;
};

export type ImportSource<T = string> = {
Expand Down Expand Up @@ -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) {
Expand Down
19 changes: 13 additions & 6 deletions packages/plugins/typescript/gql-tag-operations/src/index.ts
Expand Up @@ -25,21 +25,22 @@ export const plugin: PluginFunction<{
sourcesWithOperations: Array<SourceWithOperations>;
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`,
Expand All @@ -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,
Expand Down Expand Up @@ -84,7 +85,11 @@ function getDocumentRegistryChunk(sourcesWithOperations: Array<SourceWithOperati

type Mode = 'lookup' | 'augmented';

function getGqlOverloadChunk(sourcesWithOperations: Array<SourceWithOperations>, mode: Mode) {
function getGqlOverloadChunk(
sourcesWithOperations: Array<SourceWithOperations>,
mode: Mode,
emitLegacyCommonJSImports?: boolean
) {
const lines = new Set<string>();

// We intentionally don't use a <T extends keyof typeof documents> generic, because TS
Expand All @@ -94,7 +99,9 @@ function getGqlOverloadChunk(sourcesWithOperations: Array<SourceWithOperations>,
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`);
}

Expand Down
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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 = {
Expand Down

1 comment on commit 2cbcbb3

@vercel
Copy link

@vercel vercel bot commented on 2cbcbb3 Jul 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.