Skip to content

Commit 68bb30e

Browse files
authored
feat: attach .js extension to all relative/absolute file imports/exports (#8034)
* fix: attach .js extension to all gql-tag-operation files * feat: attach .js extension to all imports generated by near-operation-preset * chore: add changesets * chore: add test for esm import path * fix: attach .js extension for relative import emitted by gql-tag-preset * feat: handle oclif plugin
1 parent 1de927e commit 68bb30e

File tree

31 files changed

+111
-57
lines changed

31 files changed

+111
-57
lines changed

‎.changeset/angry-elephants-learn.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@graphql-codegen/visitor-plugin-common': minor
3+
---
4+
5+
Attach `.js` extension to imports starting with either a `.` or `/` character.

‎.changeset/fuzzy-elephants-ring.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@graphql-codegen/gql-tag-operations-preset': minor
3+
'@graphql-codegen/near-operation-file-preset': minor
4+
'@graphql-codegen/typescript-oclif': minor
5+
---
6+
7+
Attach `.js` extension to relative file imports for compliance with ESM module resolution. Since in CommonJS the `.js` extension is optional, this is not a breaking change.
8+
9+
If you have path configuration within your configuration, consider attaching `.js` if you are migrating to ESM.
10+
11+
```yml
12+
mappers:
13+
MyOtherType: './my-file.js#MyCustomOtherType',
14+
```

‎dev-test/githunt/__generated__/comment.query.stencil-component.tsx

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dev-test/githunt/__generated__/feed-entry.fragment.stencil-component.tsx

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dev-test/githunt/__generated__/feed.query.stencil-component.tsx

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dev-test/githunt/__generated__/submit-comment.mutation.stencil-component.tsx

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
export * from './gql';
2-
export * from './fragment-masking';
1+
export * from './gql.js';
2+
export * from './fragment-masking.js';
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
export * from './gql';
2-
export * from './fragment-masking';
1+
export * from './gql.js';
2+
export * from './fragment-masking.js';

‎dev-test/gql-tag-operations-urql/gql/gql.d.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/
44
declare module '@urql/core' {
55
export function gql(
66
source: '\n query Foo {\n Tweets {\n id\n }\n }\n'
7-
): typeof import('./graphql').FooDocument;
7+
): typeof import('./graphql.js').FooDocument;
88
export function gql(
99
source: '\n fragment Lel on Tweet {\n id\n body\n }\n'
10-
): typeof import('./graphql').LelFragmentDoc;
10+
): typeof import('./graphql.js').LelFragmentDoc;
1111
export function gql(
1212
source: '\n query Bar {\n Tweets {\n ...Lel\n }\n }\n'
13-
): typeof import('./graphql').BarDocument;
13+
): typeof import('./graphql.js').BarDocument;
1414
export function gql(source: string): unknown;
1515

1616
export type DocumentType<TDocumentNode extends DocumentNode<any, any>> = TDocumentNode extends DocumentNode<
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export * from './gql';
1+
export * from './gql.js';

‎dev-test/star-wars/__generated__/CreateReviewForEpisode.tsx

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dev-test/star-wars/__generated__/HeroAndFriendsNames.tsx

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dev-test/star-wars/__generated__/HeroAppearsIn.tsx

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dev-test/star-wars/__generated__/HeroDetails.tsx

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dev-test/star-wars/__generated__/HeroDetailsFragment.tsx

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dev-test/star-wars/__generated__/HeroDetailsWithFragment.tsx

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dev-test/star-wars/__generated__/HeroName.tsx

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dev-test/star-wars/__generated__/HeroNameConditional.tsx

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dev-test/star-wars/__generated__/HeroParentTypeDependentField.tsx

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dev-test/star-wars/__generated__/HeroTypeDependentAliasedField.tsx

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dev-test/star-wars/__generated__/HumanFields.tsx

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dev-test/star-wars/__generated__/HumanWithNullWeight.tsx

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dev-test/star-wars/__generated__/TwoHeroes.tsx

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/plugins/other/visitor-plugin-common/src/imports.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@ export function generateImportStatement(statement: ImportDeclaration): string {
5858
: '*';
5959
const importAlias = importSource.namespace ? ` as ${importSource.namespace}` : '';
6060
const importStatement = typesImport ? 'import type' : 'import';
61-
return `${importStatement} ${importNames}${importAlias} from '${importPath}';${importAlias ? '\n' : ''}`;
61+
const importExtension = importPath.startsWith('/') || importPath.startsWith('.') ? '.js' : '';
62+
return `${importStatement} ${importNames}${importAlias} from '${importPath}${importExtension}';${
63+
importAlias ? '\n' : ''
64+
}`;
6265
}
6366

6467
function resolveImportPath(baseDir: string, outputPath: string, sourcePath: string) {

‎packages/plugins/typescript/gql-tag-operations/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ function getGqlOverloadChunk(sourcesWithOperations: Array<SourceWithOperations>,
9494
const returnType =
9595
mode === 'lookup'
9696
? `(typeof documents)[${JSON.stringify(originalString)}]`
97-
: `typeof import('./graphql').${operations[0].initialName}`;
97+
: `typeof import('./graphql.js').${operations[0].initialName}`;
9898
lines.add(`export function gql(source: ${JSON.stringify(originalString)}): ${returnType};\n`);
9999
}
100100

‎packages/plugins/typescript/oclif/src/visitor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export class GraphQLRequestVisitor extends ClientSideBaseVisitor<Config, ClientS
3131
super(schema, fragments, rawConfig, {});
3232
this._info = info;
3333

34-
const { handlerPath = '../../handler' } = rawConfig;
34+
const { handlerPath = '../../handler.js' } = rawConfig;
3535

3636
// FIXME: This is taken in part from
3737
// presets/near-operation-file/src/index.ts:139. How do I build a path relative to the outputFile in the same way?

‎packages/plugins/typescript/resolvers/tests/mapping.spec.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,22 @@ describe('ResolversTypes', () => {
706706
spy.mockRestore();
707707
});
708708

709+
it('Should generate basic type resolvers with external mappers using a .js extension', async () => {
710+
const result = (await plugin(
711+
schema,
712+
[],
713+
{
714+
noSchemaStitching: true,
715+
mappers: {
716+
MyOtherType: './my-file.js#MyCustomOtherType',
717+
},
718+
},
719+
{ outputFile: '' }
720+
)) as Types.ComplexPluginOutput;
721+
722+
expect(result.prepend).toContain(`import { MyCustomOtherType } from './my-file.js';`);
723+
});
724+
709725
it('Should generate basic type resolvers with external mappers', async () => {
710726
const result = (await plugin(
711727
schema,

‎packages/presets/gql-tag-operations/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ export const preset: Types.OutputPreset<GqlTagConfig> = {
160160
},
161161
plugins: [
162162
{
163-
[`add`]: { content: reexports.map(moduleName => `export * from "./${moduleName}"`).join('\n') },
163+
[`add`]: { content: reexports.map(moduleName => `export * from "./${moduleName}.js"`).join('\n') },
164164
},
165165
],
166166
schema: options.schema,

‎packages/presets/gql-tag-operations/tests/gql-tag-operations.spec.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ describe('gql-tag-operations-preset', () => {
3030
expect(result).toHaveLength(3);
3131
// index.ts (re-exports)
3232
const indexFile = result.find(file => file.filename === 'out1/index.ts');
33-
expect(indexFile.content).toEqual('export * from "./gql"');
33+
expect(indexFile.content).toEqual('export * from "./gql.js"');
3434

3535
// gql.ts
3636
const gqlFile = result.find(file => file.filename === 'out1/gql.ts');
@@ -85,7 +85,7 @@ describe('gql-tag-operations-preset', () => {
8585
expect(result).toHaveLength(3);
8686
// index.ts (re-exports)
8787
const indexFile = result.find(file => file.filename === 'out1/index.ts');
88-
expect(indexFile.content).toEqual('export * from "./gql"');
88+
expect(indexFile.content).toEqual('export * from "./gql.js"');
8989

9090
// gql.ts
9191
const gqlFile = result.find(file => file.filename === 'out1/gql.ts');
@@ -367,9 +367,9 @@ describe('gql-tag-operations-preset', () => {
367367

368368
const gqlFile = result.find(file => file.filename === 'out1/index.ts');
369369
expect(gqlFile.content).toMatchInlineSnapshot(`
370-
"export * from \\"./gql\\"
371-
export * from \\"./fragment-masking\\""
372-
`);
370+
"export * from \\"./gql.js\\"
371+
export * from \\"./fragment-masking.js\\""
372+
`);
373373
const fragmentMaskingFile = result.find(file => file.filename === 'out1/fragment-masking.ts');
374374
expect(fragmentMaskingFile.content).toMatchInlineSnapshot(`
375375
"import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';

‎packages/presets/graphql-modules/tests/builder.spec.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,22 @@ test('should include import statement', () => {
159159
`);
160160
});
161161

162+
test('should include esm import statement', () => {
163+
const output = buildModule('test', testDoc, {
164+
importPath: '../types.js',
165+
importNamespace: 'core',
166+
encapsulate: 'none',
167+
shouldDeclare: false,
168+
rootTypes: ROOT_TYPES,
169+
baseVisitor,
170+
useGraphQLModules: true,
171+
});
172+
173+
expect(output).toBeSimilarStringTo(`
174+
import * as core from "../types.js";
175+
`);
176+
});
177+
162178
test('should work with naming conventions', () => {
163179
const output = buildModule('test', parse(`type query_root { test: ID! } schema { query: query_root }`), {
164180
importPath: '../types',

0 commit comments

Comments
 (0)