Skip to content

Commit 12fff33

Browse files
author
Akos Kitta
committed
feat: generalized Node.js error handling
gracefully handle when sketch folder has been deleted Closes #1596 Signed-off-by: Akos Kitta <[email protected]>
1 parent f6275f9 commit 12fff33

File tree

5 files changed

+46
-8
lines changed

5 files changed

+46
-8
lines changed

Diff for: arduino-ide-extension/src/electron-main/theia/electron-main-application.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
SHOW_PLOTTER_WINDOW,
2929
} from '../../common/ipc-communication';
3030
import isValidPath = require('is-valid-path');
31+
import { ErrnoException } from '../../node/utils/errors';
3132

3233
app.commandLine.appendSwitch('disable-http-cache');
3334

@@ -172,7 +173,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
172173
try {
173174
stats = await fs.stat(path);
174175
} catch (err) {
175-
if ('code' in err && err.code === 'ENOENT') {
176+
if (ErrnoException.isENOENT(err)) {
176177
return undefined;
177178
}
178179
throw err;
@@ -215,7 +216,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
215216
const resolved = await fs.realpath(resolve(cwd, maybePath));
216217
return resolved;
217218
} catch (err) {
218-
if ('code' in err && err.code === 'ENOENT') {
219+
if (ErrnoException.isENOENT(err)) {
219220
return undefined;
220221
}
221222
throw err;

Diff for: arduino-ide-extension/src/node/arduino-daemon-impl.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { BackendApplicationContribution } from '@theia/core/lib/node/backend-app
1616
import { ArduinoDaemon, NotificationServiceServer } from '../common/protocol';
1717
import { CLI_CONFIG } from './cli-config';
1818
import { getExecPath, spawnCommand } from './exec-util';
19+
import { ErrnoException } from './utils/errors';
1920

2021
@injectable()
2122
export class ArduinoDaemonImpl
@@ -184,7 +185,7 @@ export class ArduinoDaemonImpl
184185
}
185186
return false;
186187
} catch (error) {
187-
if ('code' in error && error.code === 'ENOENT') {
188+
if (ErrnoException.isENOENT(error)) {
188189
return false;
189190
}
190191
throw error;

Diff for: arduino-ide-extension/src/node/config-service-impl.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { DefaultCliConfig, CLI_CONFIG } from './cli-config';
2626
import { Deferred } from '@theia/core/lib/common/promise-util';
2727
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
2828
import { deepClone } from '@theia/core';
29+
import { ErrnoException } from './utils/errors';
2930

3031
const deepmerge = require('deepmerge');
3132

@@ -146,7 +147,7 @@ export class ConfigServiceImpl
146147
const fallbackModel = await this.getFallbackCliConfig();
147148
return deepmerge(fallbackModel, model) as DefaultCliConfig;
148149
} catch (error) {
149-
if ('code' in error && error.code === 'ENOENT') {
150+
if (ErrnoException.isENOENT(error)) {
150151
if (initializeIfAbsent) {
151152
await this.initCliConfigTo(dirname(cliConfigPath));
152153
return this.loadCliConfig(false);

Diff for: arduino-ide-extension/src/node/sketches-service-impl.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import {
3333
TempSketchPrefix,
3434
} from './is-temp-sketch';
3535
import { join } from 'path';
36+
import { ErrnoException } from './utils/errors';
3637

3738
const RecentSketches = 'recent-sketches.json';
3839
const DefaultIno = `void setup() {
@@ -278,7 +279,7 @@ export class SketchesServiceImpl
278279
);
279280
}
280281
} catch (err) {
281-
if ('code' in err && err.code === 'ENOENT') {
282+
if (ErrnoException.isENOENT(err)) {
282283
this.logger.debug(
283284
`<<< '${RecentSketches}' does not exist yet. This is normal behavior. Falling back to empty data.`
284285
);
@@ -666,7 +667,7 @@ export class SketchesServiceImpl
666667

667668
return this.tryParse(raw);
668669
} catch (err) {
669-
if ('code' in err && err.code === 'ENOENT') {
670+
if (ErrnoException.isENOENT(err)) {
670671
return undefined;
671672
}
672673
throw err;
@@ -695,7 +696,7 @@ export class SketchesServiceImpl
695696
});
696697
this.inoContent.resolve(inoContent);
697698
} catch (err) {
698-
if ('code' in err && err.code === 'ENOENT') {
699+
if (ErrnoException.isENOENT(err)) {
699700
// Ignored. The custom `.ino` blueprint file is optional.
700701
} else {
701702
throw err;
@@ -763,7 +764,7 @@ async function isInvalidSketchNameError(
763764
.map((name) => path.join(requestSketchPath, name))[0]
764765
);
765766
} catch (err) {
766-
if ('code' in err && err.code === 'ENOTDIR') {
767+
if (ErrnoException.isENOENT(err) || ErrnoException.isENOTDIR(err)) {
767768
return undefined;
768769
}
769770
throw err;

Diff for: arduino-ide-extension/src/node/utils/errors.ts

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
export type ErrnoException = Error & { code: string; errno: number };
2+
export namespace ErrnoException {
3+
export function is(arg: unknown): arg is ErrnoException {
4+
if (arg instanceof Error) {
5+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
6+
const error = arg as any;
7+
return (
8+
'code' in error &&
9+
'errno' in error &&
10+
typeof error['code'] === 'string' &&
11+
typeof error['errno'] === 'number'
12+
);
13+
}
14+
return false;
15+
}
16+
17+
/**
18+
* (No such file or directory): Commonly raised by `fs` operations to indicate that a component of the specified pathname does not exist — no entity (file or directory) could be found by the given path.
19+
*/
20+
export function isENOENT(
21+
arg: unknown
22+
): arg is ErrnoException & { code: 'ENOENT' } {
23+
return is(arg) && arg.code === 'ENOENT';
24+
}
25+
26+
/**
27+
* (Not a directory): A component of the given pathname existed, but was not a directory as expected. Commonly raised by `fs.readdir`.
28+
*/
29+
export function isENOTDIR(
30+
arg: unknown
31+
): arg is ErrnoException & { code: 'ENOTDIR' } {
32+
return is(arg) && arg.code === 'ENOTDIR';
33+
}
34+
}

0 commit comments

Comments
 (0)