mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-23 19:26:33 +00:00
Don't use lodash in child-writer.js
Changelog-entry: Don't use lodash in child-writer.js Change-type: patch
This commit is contained in:
parent
76fa698995
commit
f2a37079eb
@ -244,7 +244,6 @@ export async function performWrite(
|
||||
title: 'The writer process ended unexpectedly',
|
||||
description:
|
||||
'Please try again, and contact the Etcher team if the problem persists',
|
||||
code: 'ECHILDDIED',
|
||||
}),
|
||||
);
|
||||
return;
|
||||
|
@ -17,7 +17,6 @@
|
||||
import { Drive as DrivelistDrive } from 'drivelist';
|
||||
import * as sdk from 'etcher-sdk';
|
||||
import { cleanupTmpFiles } from 'etcher-sdk/build/tmp';
|
||||
import * as _ from 'lodash';
|
||||
import * as ipc from 'node-ipc';
|
||||
|
||||
import { toJSON } from '../../shared/errors';
|
||||
@ -223,14 +222,14 @@ ipc.connectTo(IPC_SERVER_ID, () => {
|
||||
});
|
||||
};
|
||||
|
||||
const destinations = _.map(options.destinations, 'device');
|
||||
const destinations = options.destinations.map((d) => d.device);
|
||||
log(`Image: ${options.imagePath}`);
|
||||
log(`Devices: ${destinations.join(', ')}`);
|
||||
log(`Umount on success: ${options.unmountOnSuccess}`);
|
||||
log(`Validate on success: ${options.validateWriteOnSuccess}`);
|
||||
log(`Auto blockmapping: ${options.autoBlockmapping}`);
|
||||
log(`Decompress first: ${options.decompressFirst}`);
|
||||
const dests = _.map(options.destinations, (destination) => {
|
||||
const dests = options.destinations.map((destination) => {
|
||||
return new sdk.sourceDestination.BlockDevice({
|
||||
drive: destination,
|
||||
unmountOnSuccess: options.unmountOnSuccess,
|
||||
@ -261,7 +260,7 @@ ipc.connectTo(IPC_SERVER_ID, () => {
|
||||
onFail,
|
||||
});
|
||||
log(`Finish: ${results.bytesWritten}`);
|
||||
results.errors = _.map(results.errors, (error) => {
|
||||
results.errors = results.errors.map((error) => {
|
||||
return toJSON(error);
|
||||
});
|
||||
ipc.of[IPC_SERVER_ID].emit('done', { results });
|
||||
|
@ -14,48 +14,37 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as _ from 'lodash';
|
||||
|
||||
function createErrorDetails(options: {
|
||||
title: string | ((error: Error & { path: string }) => string);
|
||||
description: string;
|
||||
}): {
|
||||
title: (error: Error & { path: string }) => string;
|
||||
description: (error: Error & { path: string }) => string;
|
||||
} {
|
||||
return _.pick(
|
||||
_.mapValues(options, (value) => {
|
||||
return _.isFunction(value) ? value : _.constant(value);
|
||||
}),
|
||||
['title', 'description'],
|
||||
);
|
||||
}
|
||||
export type ErrorWithPath = Error & {
|
||||
path?: string;
|
||||
code?: keyof typeof HUMAN_FRIENDLY;
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Human-friendly error messages
|
||||
*/
|
||||
export const HUMAN_FRIENDLY = {
|
||||
ENOENT: createErrorDetails({
|
||||
title: (error: Error & { path: string }) => {
|
||||
ENOENT: {
|
||||
title: (error: ErrorWithPath) => {
|
||||
return `No such file or directory: ${error.path}`;
|
||||
},
|
||||
description: "The file you're trying to access doesn't exist",
|
||||
}),
|
||||
EPERM: createErrorDetails({
|
||||
title: "You're not authorized to perform this operation",
|
||||
description: 'Please ensure you have necessary permissions for this task',
|
||||
}),
|
||||
EACCES: createErrorDetails({
|
||||
title: "You don't have access to this resource",
|
||||
description:
|
||||
description: () => "The file you're trying to access doesn't exist",
|
||||
},
|
||||
EPERM: {
|
||||
title: () => "You're not authorized to perform this operation",
|
||||
description: () =>
|
||||
'Please ensure you have necessary permissions for this task',
|
||||
},
|
||||
EACCES: {
|
||||
title: () => "You don't have access to this resource",
|
||||
description: () =>
|
||||
'Please ensure you have necessary permissions to access this resource',
|
||||
}),
|
||||
ENOMEM: createErrorDetails({
|
||||
title: 'Your system ran out of memory',
|
||||
description:
|
||||
},
|
||||
ENOMEM: {
|
||||
title: () => 'Your system ran out of memory',
|
||||
description: () =>
|
||||
'Please make sure your system has enough available memory for this task',
|
||||
}),
|
||||
};
|
||||
},
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* @summary Get user friendly property from an error
|
||||
@ -71,19 +60,21 @@ export const HUMAN_FRIENDLY = {
|
||||
* }
|
||||
*/
|
||||
function getUserFriendlyMessageProperty(
|
||||
error: Error,
|
||||
error: ErrorWithPath,
|
||||
property: 'title' | 'description',
|
||||
): string | null {
|
||||
const code = _.get(error, ['code']);
|
||||
|
||||
if (_.isNil(code) || !_.isString(code)) {
|
||||
return null;
|
||||
): string | undefined {
|
||||
if (typeof error.code !== 'string') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return _.invoke(HUMAN_FRIENDLY, [code, property], error);
|
||||
return HUMAN_FRIENDLY[error.code]?.[property]?.(error);
|
||||
}
|
||||
|
||||
const isBlank = _.flow([_.trim, _.isEmpty]);
|
||||
function isBlank(s: string | number | null | undefined) {
|
||||
if (typeof s === 'number') {
|
||||
s = s.toString();
|
||||
}
|
||||
return (s ?? '').trim() === '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get the title of an error
|
||||
@ -92,23 +83,19 @@ const isBlank = _.flow([_.trim, _.isEmpty]);
|
||||
* Try to get as much information as possible about the error
|
||||
* rather than falling back to generic messages right away.
|
||||
*/
|
||||
export function getTitle(error: Error): string {
|
||||
if (!_.isError(error) && !_.isPlainObject(error) && !_.isNil(error)) {
|
||||
return _.toString(error);
|
||||
}
|
||||
|
||||
export function getTitle(error: ErrorWithPath): string {
|
||||
const codeTitle = getUserFriendlyMessageProperty(error, 'title');
|
||||
if (!_.isNil(codeTitle)) {
|
||||
if (codeTitle !== undefined) {
|
||||
return codeTitle;
|
||||
}
|
||||
|
||||
const message = _.get(error, ['message']);
|
||||
const message = error.message;
|
||||
if (!isBlank(message)) {
|
||||
return message;
|
||||
}
|
||||
|
||||
const code = _.get(error, ['code']);
|
||||
if (!_.isNil(code) && !isBlank(code)) {
|
||||
const code = error.code;
|
||||
if (!isBlank(code)) {
|
||||
return `Error code: ${code}`;
|
||||
}
|
||||
|
||||
@ -119,40 +106,19 @@ export function getTitle(error: Error): string {
|
||||
* @summary Get the description of an error
|
||||
*/
|
||||
export function getDescription(
|
||||
error: Error & { description?: string },
|
||||
options: { userFriendlyDescriptionsOnly?: boolean } = {},
|
||||
error: ErrorWithPath & { description?: string },
|
||||
): string {
|
||||
_.defaults(options, {
|
||||
userFriendlyDescriptionsOnly: false,
|
||||
});
|
||||
|
||||
if (!_.isError(error) && !_.isPlainObject(error)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!isBlank(error.description)) {
|
||||
return error.description as string;
|
||||
}
|
||||
|
||||
const codeDescription = getUserFriendlyMessageProperty(error, 'description');
|
||||
if (!_.isNil(codeDescription)) {
|
||||
if (codeDescription !== undefined) {
|
||||
return codeDescription;
|
||||
}
|
||||
|
||||
if (options.userFriendlyDescriptionsOnly) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (error.stack) {
|
||||
return error.stack;
|
||||
}
|
||||
|
||||
if (_.isEmpty(error)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const INDENTATION_SPACES = 2;
|
||||
return JSON.stringify(error, null, INDENTATION_SPACES);
|
||||
return JSON.stringify(error, null, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -162,24 +128,24 @@ export function createError(options: {
|
||||
title: string;
|
||||
description?: string;
|
||||
report?: boolean;
|
||||
code?: string;
|
||||
}): Error & { description?: string; report?: boolean; code?: string } {
|
||||
code?: keyof typeof HUMAN_FRIENDLY;
|
||||
}): ErrorWithPath & { description?: string; report?: boolean } {
|
||||
if (isBlank(options.title)) {
|
||||
throw new Error(`Invalid error title: ${options.title}`);
|
||||
}
|
||||
|
||||
const error: Error & {
|
||||
const error: ErrorWithPath & {
|
||||
description?: string;
|
||||
report?: boolean;
|
||||
code?: string;
|
||||
} = new Error(options.title);
|
||||
error.description = options.description;
|
||||
|
||||
if (!_.isNil(options.report) && !options.report) {
|
||||
if (options.report === false) {
|
||||
error.report = false;
|
||||
}
|
||||
|
||||
if (!_.isNil(options.code)) {
|
||||
if (options.code !== undefined) {
|
||||
error.code = options.code;
|
||||
}
|
||||
|
||||
@ -198,7 +164,7 @@ export function createError(options: {
|
||||
export function createUserError(options: {
|
||||
title: string;
|
||||
description: string;
|
||||
code?: string;
|
||||
code?: keyof typeof HUMAN_FRIENDLY;
|
||||
}): Error {
|
||||
return createError({
|
||||
title: options.title,
|
||||
@ -208,13 +174,6 @@ export function createUserError(options: {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Check if an error is an user error
|
||||
*/
|
||||
export function isUserError(error: Error & { report?: boolean }): boolean {
|
||||
return _.isNil(error.report) ? false : !error.report;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Convert an Error object to a JSON object
|
||||
* @function
|
||||
@ -260,5 +219,5 @@ export function toJSON(
|
||||
* @summary Convert a JSON object to an Error object
|
||||
*/
|
||||
export function fromJSON(json: any): Error {
|
||||
return _.assign(new Error(json.message), json);
|
||||
return Object.assign(new Error(json.message), json);
|
||||
}
|
||||
|
7
npm-shrinkwrap.json
generated
7
npm-shrinkwrap.json
generated
@ -7019,9 +7019,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"etcher-sdk": {
|
||||
"version": "4.1.20",
|
||||
"resolved": "https://registry.npmjs.org/etcher-sdk/-/etcher-sdk-4.1.20.tgz",
|
||||
"integrity": "sha512-cVjAifAKKYDc9SNMICnK4YWXHz3uGlwKgYOkRykPATxkwhVYfaW6wlhV2DFqbfkps+l4si7iC82Tz1RaV0VjHA==",
|
||||
"version": "4.1.21",
|
||||
"resolved": "https://registry.npmjs.org/etcher-sdk/-/etcher-sdk-4.1.21.tgz",
|
||||
"integrity": "sha512-jQzKZxYkUA9+rsmelr6U1d2pBwiRw6AGM//a8EnqL1Aorq44xq2BZ59BomDwofzMJ5RCGEJjgOmi7/w+eUVVvg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@balena/udif": "^1.0.3",
|
||||
@ -7035,7 +7035,6 @@
|
||||
"drivelist": "^9.0.0",
|
||||
"file-disk": "^8.0.0",
|
||||
"file-type": "^8.0.0",
|
||||
"lodash": "^4.17.19",
|
||||
"lzma-native": "^6.0.0",
|
||||
"mountutils": "^1.3.18",
|
||||
"node-raspberrypi-usbboot": "^0.2.9",
|
||||
|
@ -75,7 +75,7 @@
|
||||
"electron-notarize": "^1.0.0",
|
||||
"electron-rebuild": "^1.11.0",
|
||||
"electron-updater": "^4.3.2",
|
||||
"etcher-sdk": "^4.1.20",
|
||||
"etcher-sdk": "^4.1.21",
|
||||
"file-loader": "^6.0.0",
|
||||
"husky": "^4.2.5",
|
||||
"immutable": "^3.8.1",
|
||||
|
@ -37,54 +37,12 @@ describe('Shared: Errors', function () {
|
||||
});
|
||||
|
||||
describe('.getTitle()', function () {
|
||||
it('should accept a string', function () {
|
||||
const error = 'This is an error';
|
||||
// @ts-ignore
|
||||
expect(errors.getTitle(error)).to.equal('This is an error');
|
||||
});
|
||||
|
||||
it('should accept a number 0', function () {
|
||||
const error = 0;
|
||||
// @ts-ignore
|
||||
expect(errors.getTitle(error)).to.equal('0');
|
||||
});
|
||||
|
||||
it('should accept a number 1', function () {
|
||||
const error = 1;
|
||||
// @ts-ignore
|
||||
expect(errors.getTitle(error)).to.equal('1');
|
||||
});
|
||||
|
||||
it('should accept a number -1', function () {
|
||||
const error = -1;
|
||||
// @ts-ignore
|
||||
expect(errors.getTitle(error)).to.equal('-1');
|
||||
});
|
||||
|
||||
it('should accept an array', function () {
|
||||
const error = [0, 1, 2];
|
||||
// @ts-ignore
|
||||
expect(errors.getTitle(error)).to.equal('0,1,2');
|
||||
});
|
||||
|
||||
it('should return a generic error message if the error is an empty object', function () {
|
||||
const error = {};
|
||||
// @ts-ignore
|
||||
expect(errors.getTitle(error)).to.equal('An error ocurred');
|
||||
});
|
||||
|
||||
it('should return a generic error message if the error is undefined', function () {
|
||||
const error = undefined;
|
||||
// @ts-ignore
|
||||
expect(errors.getTitle(error)).to.equal('An error ocurred');
|
||||
});
|
||||
|
||||
it('should return a generic error message if the error is null', function () {
|
||||
const error = null;
|
||||
// @ts-ignore
|
||||
expect(errors.getTitle(error)).to.equal('An error ocurred');
|
||||
});
|
||||
|
||||
it('should return the error message', function () {
|
||||
const error = new Error('This is an error');
|
||||
expect(errors.getTitle(error)).to.equal('This is an error');
|
||||
@ -234,42 +192,6 @@ describe('Shared: Errors', function () {
|
||||
});
|
||||
|
||||
describe('.getDescription()', function () {
|
||||
it('should return an empty string if the error is a string', function () {
|
||||
const error = 'My error';
|
||||
// @ts-ignore
|
||||
expect(errors.getDescription(error)).to.equal('');
|
||||
});
|
||||
|
||||
it('should return an empty string if the error is a number', function () {
|
||||
const error = 0;
|
||||
// @ts-ignore
|
||||
expect(errors.getDescription(error)).to.equal('');
|
||||
});
|
||||
|
||||
it('should return an empty string if the error is an array', function () {
|
||||
const error = [1, 2, 3];
|
||||
// @ts-ignore
|
||||
expect(errors.getDescription(error)).to.equal('');
|
||||
});
|
||||
|
||||
it('should return an empty string if the error is undefined', function () {
|
||||
const error = undefined;
|
||||
// @ts-ignore
|
||||
expect(errors.getDescription(error)).to.equal('');
|
||||
});
|
||||
|
||||
it('should return an empty string if the error is null', function () {
|
||||
const error = null;
|
||||
// @ts-ignore
|
||||
expect(errors.getDescription(error)).to.equal('');
|
||||
});
|
||||
|
||||
it('should return an empty string if the error is an empty object', function () {
|
||||
const error = {};
|
||||
// @ts-ignore
|
||||
expect(errors.getDescription(error)).to.equal('');
|
||||
});
|
||||
|
||||
it('should understand an error-like object with a description', function () {
|
||||
const error = {
|
||||
description: 'My description',
|
||||
@ -384,122 +306,26 @@ describe('Shared: Errors', function () {
|
||||
describe('given userFriendlyDescriptionsOnly is false', function () {
|
||||
it('should return the stack for a basic error', function () {
|
||||
const error = new Error('Foo');
|
||||
expect(
|
||||
errors.getDescription(error, {
|
||||
userFriendlyDescriptionsOnly: false,
|
||||
}),
|
||||
).to.equal(error.stack);
|
||||
expect(errors.getDescription(error)).to.equal(error.stack);
|
||||
});
|
||||
|
||||
it('should return the stack if the description is an empty string', function () {
|
||||
const error = new Error('Foo');
|
||||
// @ts-ignore
|
||||
error.description = '';
|
||||
expect(
|
||||
errors.getDescription(error, {
|
||||
userFriendlyDescriptionsOnly: false,
|
||||
}),
|
||||
).to.equal(error.stack);
|
||||
expect(errors.getDescription(error)).to.equal(error.stack);
|
||||
});
|
||||
|
||||
it('should return the stack if the description is a blank string', function () {
|
||||
const error = new Error('Foo');
|
||||
// @ts-ignore
|
||||
error.description = ' ';
|
||||
expect(
|
||||
errors.getDescription(error, {
|
||||
userFriendlyDescriptionsOnly: false,
|
||||
}),
|
||||
).to.equal(error.stack);
|
||||
});
|
||||
});
|
||||
|
||||
describe('given userFriendlyDescriptionsOnly is true', function () {
|
||||
it('should return an empty string for a basic error', function () {
|
||||
const error = new Error('Foo');
|
||||
expect(
|
||||
errors.getDescription(error, {
|
||||
userFriendlyDescriptionsOnly: true,
|
||||
}),
|
||||
).to.equal('');
|
||||
});
|
||||
|
||||
it('should return an empty string if the description is an empty string', function () {
|
||||
const error = new Error('Foo');
|
||||
// @ts-ignore
|
||||
error.description = '';
|
||||
expect(
|
||||
errors.getDescription(error, {
|
||||
userFriendlyDescriptionsOnly: true,
|
||||
}),
|
||||
).to.equal('');
|
||||
});
|
||||
|
||||
it('should return an empty string if the description is a blank string', function () {
|
||||
const error = new Error('Foo');
|
||||
// @ts-ignore
|
||||
error.description = ' ';
|
||||
expect(
|
||||
errors.getDescription(error, {
|
||||
userFriendlyDescriptionsOnly: true,
|
||||
}),
|
||||
).to.equal('');
|
||||
expect(errors.getDescription(error)).to.equal(error.stack);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('.createError()', function () {
|
||||
it('should not be a user error', function () {
|
||||
const error = errors.createError({
|
||||
title: 'Foo',
|
||||
description: 'Something happened',
|
||||
});
|
||||
|
||||
expect(errors.isUserError(error)).to.be.false;
|
||||
});
|
||||
|
||||
it('should be a user error if `options.report` is false', function () {
|
||||
const error = errors.createError({
|
||||
title: 'Foo',
|
||||
description: 'Something happened',
|
||||
report: false,
|
||||
});
|
||||
|
||||
expect(errors.isUserError(error)).to.be.true;
|
||||
});
|
||||
|
||||
it('should be a user error if `options.report` evaluates to false', function () {
|
||||
const error = errors.createError({
|
||||
title: 'Foo',
|
||||
description: 'Something happened',
|
||||
// @ts-ignore
|
||||
report: 0,
|
||||
});
|
||||
|
||||
expect(errors.isUserError(error)).to.be.true;
|
||||
});
|
||||
|
||||
it('should not be a user error if `options.report` is true', function () {
|
||||
const error = errors.createError({
|
||||
title: 'Foo',
|
||||
description: 'Something happened',
|
||||
report: true,
|
||||
});
|
||||
|
||||
expect(errors.isUserError(error)).to.be.false;
|
||||
});
|
||||
|
||||
it('should not be a user error if `options.report` evaluates to true', function () {
|
||||
const error = errors.createError({
|
||||
title: 'Foo',
|
||||
description: 'Something happened',
|
||||
// @ts-ignore
|
||||
report: 1,
|
||||
});
|
||||
|
||||
expect(errors.isUserError(error)).to.be.false;
|
||||
});
|
||||
|
||||
it('should be an instance of Error', function () {
|
||||
const error = errors.createError({
|
||||
title: 'Foo',
|
||||
@ -523,10 +349,10 @@ describe('Shared: Errors', function () {
|
||||
const error = errors.createError({
|
||||
title: 'Foo',
|
||||
description: 'Something happened',
|
||||
code: 'HELLO',
|
||||
code: 'ENOENT',
|
||||
});
|
||||
|
||||
expect(error.code).to.equal('HELLO');
|
||||
expect(error.code).to.equal('ENOENT');
|
||||
});
|
||||
|
||||
it('should correctly add only a title', function () {
|
||||
@ -590,15 +416,6 @@ describe('Shared: Errors', function () {
|
||||
});
|
||||
|
||||
describe('.createUserError()', function () {
|
||||
it('should be a user error', function () {
|
||||
const error = errors.createUserError({
|
||||
title: 'Foo',
|
||||
description: 'Something happened',
|
||||
});
|
||||
|
||||
expect(errors.isUserError(error)).to.be.true;
|
||||
});
|
||||
|
||||
it('should be an instance of Error', function () {
|
||||
const error = errors.createUserError({
|
||||
title: 'Foo',
|
||||
@ -632,11 +449,11 @@ describe('Shared: Errors', function () {
|
||||
// @ts-ignore
|
||||
const error = errors.createUserError({
|
||||
title: 'Foo',
|
||||
code: 'HELLO',
|
||||
code: 'ENOENT',
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
expect(error.code).to.equal('HELLO');
|
||||
expect(error.code).to.equal('ENOENT');
|
||||
});
|
||||
|
||||
it('should ignore an empty description', function () {
|
||||
@ -692,26 +509,6 @@ describe('Shared: Errors', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('.isUserError()', function () {
|
||||
_.each([0, '', false], (value) => {
|
||||
it(`should return true if report equals ${value}`, function () {
|
||||
const error = new Error('foo bar');
|
||||
// @ts-ignore
|
||||
error.report = value;
|
||||
expect(errors.isUserError(error)).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
_.each([undefined, null, true, 1, 3, 'foo'], (value) => {
|
||||
it(`should return false if report equals ${value}`, function () {
|
||||
const error = new Error('foo bar');
|
||||
// @ts-ignore
|
||||
error.report = value;
|
||||
expect(errors.isUserError(error)).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('.toJSON()', function () {
|
||||
it('should convert a simple error', function () {
|
||||
const error = new Error('My error');
|
||||
|
Loading…
x
Reference in New Issue
Block a user