Skip to content

Commit 15c26eb

Browse files
dimaMachinaTallTed
andauthoredApr 14, 2023
[ESLint] enable promise/prefer-await-to-then for non React packages (#3120)
Co-authored-by: Ted Thibodeau Jr <tthibodeau@openlinksw.com>
1 parent b901d61 commit 15c26eb

File tree

10 files changed

+91
-80
lines changed

10 files changed

+91
-80
lines changed
 

‎.changeset/tasty-glasses-report.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'@graphiql/toolkit': patch
3+
'graphql-language-service-server': patch
4+
'monaco-graphql': patch
5+
'vscode-graphql': patch
6+
'vscode-graphql-execution': patch
7+
---
8+
9+
prefer await to then

‎.eslintrc.js

+8
Original file line numberDiff line numberDiff line change
@@ -366,5 +366,13 @@ module.exports = {
366366
'import/no-unresolved': ['error', { ignore: ['^node:', 'vscode'] }],
367367
},
368368
},
369+
{
370+
files: ['packages/**'],
371+
// ignore React packages because it's ugly to have `async IIFE` inside `useEffect`
372+
excludedFiles: ['packages/graphiql/**', 'packages/graphiql-react/**'],
373+
rules: {
374+
'promise/prefer-await-to-then': 'error',
375+
},
376+
},
369377
],
370378
};

‎packages/graphiql-toolkit/src/async-helpers/index.ts

+23-31
Original file line numberDiff line numberDiff line change
@@ -52,42 +52,34 @@ export function isAsyncIterable(
5252
);
5353
}
5454

55-
function asyncIterableToPromise<T>(
55+
async function asyncIterableToPromise<T>(
5656
input: AsyncIterable<T> | AsyncIterableIterator<T>,
5757
): Promise<T> {
58-
return new Promise((resolve, reject) => {
59-
// Also support AsyncGenerator on Safari iOS.
60-
// As mentioned in the isAsyncIterable function there is no Symbol.asyncIterator available
61-
// so every AsyncIterable must be implemented using AsyncGenerator.
62-
const iteratorReturn = (
63-
'return' in input ? input : input[Symbol.asyncIterator]()
64-
).return?.bind(input);
65-
const iteratorNext = (
66-
'next' in input ? input : input[Symbol.asyncIterator]()
67-
).next.bind(input);
58+
// Also support AsyncGenerator on Safari iOS.
59+
// As mentioned in the isAsyncIterable function, there is no Symbol.asyncIterator available,
60+
// so every AsyncIterable must be implemented using AsyncGenerator.
61+
const iteratorReturn = (
62+
'return' in input ? input : input[Symbol.asyncIterator]()
63+
).return?.bind(input);
64+
const iteratorNext = (
65+
'next' in input ? input : input[Symbol.asyncIterator]()
66+
).next.bind(input);
6867

69-
iteratorNext()
70-
.then(result => {
71-
resolve(result.value);
72-
// ensure cleanup
73-
void iteratorReturn?.();
74-
})
75-
.catch(err => {
76-
reject(err);
77-
});
78-
});
68+
const result = await iteratorNext();
69+
// ensure cleanup
70+
void iteratorReturn?.();
71+
return result.value;
7972
}
8073

81-
export function fetcherReturnToPromise(
74+
export async function fetcherReturnToPromise(
8275
fetcherResult: FetcherReturnType,
8376
): Promise<FetcherResult> {
84-
return Promise.resolve(fetcherResult).then(result => {
85-
if (isAsyncIterable(result)) {
86-
return asyncIterableToPromise(result);
87-
}
88-
if (isObservable(result)) {
89-
return observableToPromise(result);
90-
}
91-
return result;
92-
});
77+
const result = await fetcherResult;
78+
if (isAsyncIterable(result)) {
79+
return asyncIterableToPromise(result);
80+
}
81+
if (isObservable(result)) {
82+
return observableToPromise(result);
83+
}
84+
return result;
9385
}

‎packages/graphql-language-service-cli/src/cli.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -117,21 +117,22 @@ if (command === 'server') {
117117
});
118118

119119
const options: { [key: string]: any } = {};
120-
if (argv?.port) {
120+
if (argv.port) {
121121
options.port = argv.port;
122122
}
123-
if (argv?.method) {
123+
if (argv.method) {
124124
options.method = argv.method;
125125
}
126-
if (argv?.configDir) {
126+
if (argv.configDir) {
127127
options.configDir = argv.configDir;
128128
}
129+
// eslint-disable-next-line promise/prefer-await-to-then -- don't know if I can use top level await here
129130
startServer(options).catch(error => {
130131
const logger = new Logger();
131132
logger.error(String(error));
132133
});
133134
} else {
134-
client(command as string, argv as { [key: string]: string });
135+
client(command as string, argv as Record<string, string>);
135136
}
136137

137138
// Exit the process when stream closes from remote end.

‎packages/graphql-language-service-server/src/GraphQLCache.ts

+26-27
Original file line numberDiff line numberDiff line change
@@ -679,33 +679,32 @@ export class GraphQLCache implements GraphQLCacheInterface {
679679
const responses: GraphQLFileInfo[] = [];
680680
while (queue.length) {
681681
const chunk = queue.splice(0, MAX_READS);
682-
const promises = chunk.map(fileInfo =>
683-
this.promiseToReadGraphQLFile(fileInfo.filePath)
684-
.catch(error => {
685-
// eslint-disable-next-line no-console
686-
console.log('pro', error);
687-
/**
688-
* fs emits `EMFILE | ENFILE` error when there are too many
689-
* open files - this can cause some fragment files not to be
690-
* processed. Solve this case by implementing a queue to save
691-
* files failed to be processed because of `EMFILE` error,
692-
* and await on Promises created with the next batch from the
693-
* queue.
694-
*/
695-
if (error.code === 'EMFILE' || error.code === 'ENFILE') {
696-
queue.push(fileInfo);
697-
}
698-
})
699-
.then((response: GraphQLFileInfo | void) => {
700-
if (response) {
701-
responses.push({
702-
...response,
703-
mtime: fileInfo.mtime,
704-
size: fileInfo.size,
705-
});
706-
}
707-
}),
708-
);
682+
const promises = chunk.map(async fileInfo => {
683+
try {
684+
const response = await this.promiseToReadGraphQLFile(
685+
fileInfo.filePath,
686+
);
687+
responses.push({
688+
...response,
689+
mtime: fileInfo.mtime,
690+
size: fileInfo.size,
691+
});
692+
} catch (error: any) {
693+
// eslint-disable-next-line no-console
694+
console.log('pro', error);
695+
/**
696+
* fs emits `EMFILE | ENFILE` error when there are too many
697+
* open files - this can cause some fragment files not to be
698+
* processed. Solve this case by implementing a queue to save
699+
* files failed to be processed because of `EMFILE` error,
700+
* and await on Promises created with the next batch from the
701+
* queue.
702+
*/
703+
if (error.code === 'EMFILE' || error.code === 'ENFILE') {
704+
queue.push(fileInfo);
705+
}
706+
}
707+
});
709708
await Promise.all(promises); // eslint-disable-line no-await-in-loop
710709
}
711710

‎packages/graphql-language-service-server/src/__tests__/MessageProcessor-test.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -270,10 +270,10 @@ describe('MessageProcessor', () => {
270270
const params = { textDocument: initialDocument.textDocument, position };
271271

272272
// Should throw because file has been deleted from cache
273-
return messageProcessor
274-
.handleCompletionRequest(params)
275-
.then(result => expect(result).toEqual(null))
276-
.catch(() => {});
273+
try {
274+
const result = await messageProcessor.handleCompletionRequest(params);
275+
expect(result).toEqual(null);
276+
} catch {}
277277
});
278278

279279
// modified to work with jest.mock() of WatchmanClient

‎packages/graphql-language-service-server/src/startServer.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -170,22 +170,21 @@ export default async function startServer(
170170

171171
const { port, hostname } = options;
172172
const socket = net
173-
.createServer(client => {
173+
.createServer(async client => {
174174
client.setEncoding('utf8');
175175
reader = new SocketMessageReader(client);
176176
writer = new SocketMessageWriter(client);
177177
client.on('end', () => {
178178
socket.close();
179179
process.exit(0);
180180
});
181-
return initializeHandlers({
181+
const s = await initializeHandlers({
182182
reader,
183183
writer,
184184
logger,
185185
options: finalOptions,
186-
}).then(s => {
187-
s.listen();
188186
});
187+
s.listen();
189188
})
190189
.listen(port, hostname);
191190
return;

‎packages/monaco-graphql/src/initializeMode.ts

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ export function initializeMode(
2828
api = createMonacoGraphQLAPI(LANGUAGE_ID, config);
2929
(<any>languages).graphql = { api };
3030
// export to the global monaco API
31+
32+
// eslint-disable-next-line promise/prefer-await-to-then -- ignore to leave initializeMode sync
3133
void getMode().then(mode => mode.setupMode(api));
3234
}
3335

‎packages/vscode-graphql-execution/src/providers/exec-content.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,10 @@ export class GraphQLContentProvider implements TextDocumentContentProvider {
108108
enableScripts: true,
109109
};
110110

111-
this.loadProvider()
112-
.then()
113-
.catch(err => {
114-
this.html = err.toString();
115-
});
111+
// eslint-disable-next-line promise/prefer-await-to-then -- can't use async in constructor
112+
this.loadProvider().catch(err => {
113+
this.html = err.toString();
114+
});
116115
}
117116

118117
validUrlFromSchema(pathOrUrl: string) {

‎packages/vscode-graphql/src/server/index.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ import { startServer } from 'graphql-language-service-server';
66
// The npm scripts are configured to only build this once before
77
// watching the extension, so please restart the extension debugger for changes!
88

9-
const start = () => {
10-
startServer({ method: 'node' }).catch(err => {
9+
async function start() {
10+
try {
11+
await startServer({ method: 'node' });
12+
} catch (err) {
1113
// eslint-disable-next-line no-console
1214
console.error(err);
13-
});
14-
};
15+
}
16+
}
1517

16-
start();
18+
void start();

0 commit comments

Comments
 (0)
Please sign in to comment.