From 15f82c5f251a65beb7f62c88707044825af4b758 Mon Sep 17 00:00:00 2001 From: Edwin Joassart Date: Tue, 26 Sep 2023 14:20:45 +0200 Subject: [PATCH] patch: extract the loader from gui and add it to utility (required some api refactoring) --- lib/gui/app/app.ts | 40 ++- .../flash-results/flash-results.tsx | 5 +- .../source-selector/source-selector.tsx | 112 ++------ lib/gui/app/models/selection-state.ts | 1 - lib/gui/app/modules/api.ts | 35 ++- lib/gui/app/modules/drive-scanner.ts | 45 --- lib/gui/app/modules/image-writer.ts | 53 ++-- lib/gui/app/pages/main/Flash.tsx | 8 +- lib/shared/typings/source-selector.ts | 5 +- lib/util/api.ts | 23 +- lib/util/scanner.ts | 1 - npm-shrinkwrap.json | 267 ++++++++++++++++++ package.json | 1 + 13 files changed, 376 insertions(+), 220 deletions(-) delete mode 100644 lib/gui/app/modules/drive-scanner.ts diff --git a/lib/gui/app/app.ts b/lib/gui/app/app.ts index b3c7ac89bca..6b822246337 100644 --- a/lib/gui/app/app.ts +++ b/lib/gui/app/app.ts @@ -38,6 +38,8 @@ import * as windowProgress from './os/window-progress'; import MainPage from './pages/main/MainPage'; import './css/main.css'; import * as i18next from 'i18next'; +import { promises } from 'dns'; +import { SourceMetadata } from '../../shared/typings/source-selector'; window.addEventListener( 'unhandledrejection', @@ -134,25 +136,33 @@ function setDrives(drives: Dictionary) { } // Spwaning the child process without privileges to get the drives list -const apiEventHandler = (event: any) => { - switch (event.type) { - case 'drives': - setDrives(JSON.parse(event.payload)); - break; - default: - console.log('Unknown event type', event.type); - break; - } -}; - -const apiEvents = ['drives']; +// TODO: clean up this mess of exports +export let requestMetadata: any; +export let stopScanning: any; +// start the api and spawn the child process startApiAndSpawnChild({ - apiEventHandler, - apiEvents, withPrivileges: false, -}).then(({ emit }) => { +}).then(({ emit, registerHandler, terminateServer }) => { + // start scanning emit('scan'); + + // make the sourceMetada awaitable to be used on source selection + requestMetadata = async (params: any): Promise => { + emit('sourceMetadata', JSON.stringify(params)); + + return new Promise((resolve) => + registerHandler('sourceMetadata', (data: any) => { + resolve(JSON.parse(data)); + }), + ); + }; + + stopScanning = stopScanning; + + registerHandler('drives', (data: any) => { + setDrives(JSON.parse(data)); + }); }); let popupExists = false; diff --git a/lib/gui/app/components/flash-results/flash-results.tsx b/lib/gui/app/components/flash-results/flash-results.tsx index 99e66937e58..886bb2cdc91 100644 --- a/lib/gui/app/components/flash-results/flash-results.tsx +++ b/lib/gui/app/components/flash-results/flash-results.tsx @@ -139,8 +139,9 @@ export function FlashResults({ }; } & FlexProps) { const [showErrorsInfo, setShowErrorsInfo] = React.useState(false); - const allFailed = !skip && results.devices.successful === 0; - const someFailed = results.devices.failed !== 0 || errors.length !== 0; + + const allFailed = !skip && results?.devices?.successful === 0; + const someFailed = results?.devices?.failed !== 0 || errors?.length !== 0; const effectiveSpeed = bytesToMegabytes(getEffectiveSpeed(results)).toFixed( 1, ); diff --git a/lib/gui/app/components/source-selector/source-selector.tsx b/lib/gui/app/components/source-selector/source-selector.tsx index 4070b56c037..ddb45b7c9af 100644 --- a/lib/gui/app/components/source-selector/source-selector.tsx +++ b/lib/gui/app/components/source-selector/source-selector.tsx @@ -20,12 +20,13 @@ import LinkSvg from '@fortawesome/fontawesome-free/svgs/solid/link.svg'; import ExclamationTriangleSvg from '@fortawesome/fontawesome-free/svgs/solid/exclamation-triangle.svg'; import ChevronDownSvg from '@fortawesome/fontawesome-free/svgs/solid/chevron-down.svg'; import ChevronRightSvg from '@fortawesome/fontawesome-free/svgs/solid/chevron-right.svg'; -import { sourceDestination } from 'etcher-sdk'; import { ipcRenderer, IpcRendererEvent } from 'electron'; -import * as _ from 'lodash'; +import { uniqBy, isNil } from 'lodash'; import * as path from 'path'; import * as prettyBytes from 'pretty-bytes'; import * as React from 'react'; +import { requestMetadata } from '../../app'; + import { Flex, ButtonProps, @@ -46,7 +47,7 @@ import { observe } from '../../models/store'; import * as analytics from '../../modules/analytics'; import * as exceptionReporter from '../../modules/exception-reporter'; import * as osDialog from '../../os/dialog'; -import { replaceWindowsNetworkDriveLetter } from '../../os/windows-network-drives'; + import { ChangeButton, DetailsText, @@ -63,7 +64,6 @@ import ImageSvg from '../../../assets/image.svg'; import SrcSvg from '../../../assets/src.svg'; import { DriveSelector } from '../drive-selector/drive-selector'; import { DrivelistDrive } from '../../../../shared/drive-constraints'; -import axios, { AxiosRequestConfig } from 'axios'; import { isJson } from '../../../../shared/utils'; import { SourceMetadata, @@ -87,7 +87,7 @@ function normalizeRecentUrlImages(urls: any[]): URL[] { } }) .filter((url) => url !== undefined); - urls = _.uniqBy(urls, (url) => url.href); + urls = uniqBy(urls, (url) => url.href); return urls.slice(urls.length - 5); } @@ -362,43 +362,11 @@ export class SourceSelector extends React.Component< this.setState({ imageLoading: true }); await this.selectSource( imagePath, - isURL(this.normalizeImagePath(imagePath)) - ? sourceDestination.Http - : sourceDestination.File, + isURL(this.normalizeImagePath(imagePath)) ? 'Http' : 'File', ).promise; this.setState({ imageLoading: false }); } - private async createSource( - selected: string, - SourceType: Source, - auth?: Authentication, - ) { - try { - selected = await replaceWindowsNetworkDriveLetter(selected); - } catch (error: any) { - analytics.logException(error); - } - - if (isJson(decodeURIComponent(selected))) { - const config: AxiosRequestConfig = JSON.parse( - decodeURIComponent(selected), - ); - return new sourceDestination.Http({ - url: config.url!, - axiosInstance: axios.create(_.omit(config, ['url'])), - }); - } - - if (SourceType === sourceDestination.File) { - return new sourceDestination.File({ - path: selected, - }); - } - - return new sourceDestination.Http({ url: selected, auth }); - } - public normalizeImagePath(imgPath: string) { const decodedPath = decodeURIComponent(imgPath); if (isJson(decodedPath)) { @@ -427,11 +395,10 @@ export class SourceSelector extends React.Component< }, promise: (async () => { const sourcePath = isString(selected) ? selected : selected.device; - let source; let metadata: SourceMetadata | undefined; if (isString(selected)) { if ( - SourceType === sourceDestination.Http && + SourceType === 'Http' && !isURL(this.normalizeImagePath(selected)) ) { this.handleError( @@ -451,24 +418,16 @@ export class SourceSelector extends React.Component< }, }); } - source = await this.createSource(selected, SourceType, auth); - - if (cancelled) { - return; - } try { - const innerSource = await source.getInnerSource(); - if (cancelled) { - return; - } - metadata = await this.getMetadata(innerSource, selected); - if (cancelled) { - return; - } - metadata.SourceType = SourceType; + // this will send an event down the ipcMain asking for metadata + // we'll get the response through an event + + console.log('--> will request metadata'); + metadata = await requestMetadata({ selected, SourceType, auth }); + console.log('--> got metadata', metadata); - if (!metadata.hasMBR && this.state.warning === null) { + if (!metadata?.hasMBR && this.state.warning === null) { analytics.logEvent('Missing partition table', { metadata }); this.setState({ warning: { @@ -484,12 +443,6 @@ export class SourceSelector extends React.Component< messages.error.openSource(sourcePath, error.message), error, ); - } finally { - try { - await source.close(); - } catch (error: any) { - // Noop - } } } else { if (selected.partitionTableType === null) { @@ -506,13 +459,14 @@ export class SourceSelector extends React.Component< displayName: selected.displayName, description: selected.displayName, size: selected.size as SourceMetadata['size'], - SourceType: sourceDestination.BlockDevice, + SourceType: 'BlockDevice', drive: selected, }; } if (metadata !== undefined) { metadata.auth = auth; + metadata.SourceType = SourceType; selectionState.selectSource(metadata); analytics.logEvent('Select image', { // An easy way so we can quickly identify if we're making use of @@ -546,25 +500,6 @@ export class SourceSelector extends React.Component< analytics.logEvent(title, { path: sourcePath }); } - private async getMetadata( - source: sourceDestination.SourceDestination, - selected: string | DrivelistDrive, - ) { - const metadata = (await source.getMetadata()) as SourceMetadata; - const partitionTable = await source.getPartitionTable(); - if (partitionTable) { - metadata.hasMBR = true; - metadata.partitions = partitionTable.partitions; - } else { - metadata.hasMBR = false; - } - if (isString(selected)) { - metadata.extension = path.extname(selected).slice(1); - metadata.path = selected; - } - return metadata; - } - private async openImageSelector() { analytics.logEvent('Open image selector'); this.setState({ imageSelectorOpen: true }); @@ -577,7 +512,7 @@ export class SourceSelector extends React.Component< analytics.logEvent('Image selector closed'); return; } - await this.selectSource(imagePath, sourceDestination.File).promise; + await this.selectSource(imagePath, 'File').promise; } catch (error: any) { exceptionReporter.report(error); } finally { @@ -588,7 +523,7 @@ export class SourceSelector extends React.Component< private async onDrop(event: React.DragEvent) { const [file] = event.dataTransfer.files; if (file) { - await this.selectSource(file.path, sourceDestination.File).promise; + await this.selectSource(file.path, 'File').promise; } } @@ -704,7 +639,7 @@ export class SourceSelector extends React.Component< {i18next.t('cancel')} )} - {!_.isNil(imageSize) && !imageLoading && ( + {!isNil(imageSize) && !imageLoading && ( {prettyBytes(imageSize)} )} @@ -808,7 +743,7 @@ export class SourceSelector extends React.Component< let promise; ({ promise, cancel: cancelURLSelection } = this.selectSource( imageURL, - sourceDestination.Http, + 'Http', auth, )); await promise; @@ -831,10 +766,7 @@ export class SourceSelector extends React.Component< if (originalList.length) { const originalSource = originalList[0]; if (selectionImage?.drive?.device !== originalSource.device) { - this.selectSource( - originalSource, - sourceDestination.BlockDevice, - ); + this.selectSource(originalSource, 'BlockDevice'); } } else { selectionState.deselectImage(); @@ -849,7 +781,7 @@ export class SourceSelector extends React.Component< ) { return selectionState.deselectImage(); } - this.selectSource(drive, sourceDestination.BlockDevice); + this.selectSource(drive, 'BlockDevice'); } }} /> diff --git a/lib/gui/app/models/selection-state.ts b/lib/gui/app/models/selection-state.ts index 3b0c55faf81..21a29cb5737 100644 --- a/lib/gui/app/models/selection-state.ts +++ b/lib/gui/app/models/selection-state.ts @@ -52,7 +52,6 @@ export function selectSource(source: SourceMetadata) { * @summary Get all selected drives' devices */ export function getSelectedDevices(): string[] { - console.log('fullState', store.getState()); return store.getState().getIn(['selection', 'devices']).toJS(); } diff --git a/lib/gui/app/modules/api.ts b/lib/gui/app/modules/api.ts index 020238b7dea..44b72dff6e8 100644 --- a/lib/gui/app/modules/api.ts +++ b/lib/gui/app/modules/api.ts @@ -109,23 +109,19 @@ function terminateServer(server: any) { // TODO: replace the custom ipc events by one generic "message" for all communication with the backend function startApiAndSpawnChild({ - apiEvents, - apiEventHandler, withPrivileges, }: { - apiEvents: string[]; - apiEventHandler: any; withPrivileges: boolean; }): Promise { // There might be multiple Etcher instances running at // the same time, also we might spawn multiple child and api so we must ensure each IPC // server/client has a different name. - const IPC_SERVER_ID = `etcher-server-${ - process.pid - }-${Date.now()}-${Math.random()}}`; - const IPC_CLIENT_ID = `etcher-client-${ - process.pid - }-${Date.now()}-${Math.random()}}`; + const IPC_SERVER_ID = `etcher-server-${process.pid}-${Date.now()}-${ + withPrivileges ? 'privileged' : 'unprivileged' + }}}`; + const IPC_CLIENT_ID = `etcher-client-${process.pid}-${Date.now()}-${ + withPrivileges ? 'privileged' : 'unprivileged' + }}`; const IPC_SOCKET_ROOT = path.join( process.env.XDG_RUNTIME_DIR || os.tmpdir(), @@ -143,19 +139,21 @@ function startApiAndSpawnChild({ console.log(message); }); + // api to register more handlers with callbacks + const registerHandler = (event: string, handler: any) => { + ipc.server.on(event, handler); + }; + // once api is ready (means child process is connected) we pass the emit and terminate function to the caller ipc.server.on('ready', (_: any, socket) => { const emit = (channel: string, data: any) => { ipc.server.emit(socket, channel, data); }; - resolve({ emit, terminateServer: () => terminateServer(ipc.server) }); - }); - - // register all the api events we want to track, they will be handled by the apiEventHandler - apiEvents.forEach((event) => { - ipc.server.on(event, (payload) => - apiEventHandler({ type: event, payload }), - ); + resolve({ + emit, + terminateServer: () => terminateServer(ipc.server), + registerHandler, + }); }); // on api error we terminate @@ -174,6 +172,7 @@ function startApiAndSpawnChild({ IPC_SERVER_ID, IPC_SOCKET_ROOT, }); + console.log('Child process started', IPC_SERVER_ID); // this will happen if the child is spawned withPrivileges and privileges has been rejected if (results.cancelled) { reject(); diff --git a/lib/gui/app/modules/drive-scanner.ts b/lib/gui/app/modules/drive-scanner.ts deleted file mode 100644 index 84a1af3f81a..00000000000 --- a/lib/gui/app/modules/drive-scanner.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2016 balena.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as sdk from 'etcher-sdk'; -import { - Adapter, - BlockDeviceAdapter, - UsbbootDeviceAdapter, -} from 'etcher-sdk/build/scanner/adapters'; -import { geteuid, platform } from 'process'; - -const adapters: Adapter[] = [ - new BlockDeviceAdapter({ - includeSystemDrives: () => true, - }), -]; - -// Can't use permissions.isElevated() here as it returns a promise and we need to set -// module.exports = scanner right now. -if (platform !== 'linux' || geteuid() === 0) { - adapters.push(new UsbbootDeviceAdapter()); -} - -if (platform === 'win32') { - const { - DriverlessDeviceAdapter: driverless, - // tslint:disable-next-line:no-var-requires - } = require('etcher-sdk/build/scanner/adapters/driverless'); - adapters.push(new driverless()); -} - -export const scanner = new sdk.scanner.Scanner(adapters); diff --git a/lib/gui/app/modules/image-writer.ts b/lib/gui/app/modules/image-writer.ts index c640298b2f3..96d5b095582 100644 --- a/lib/gui/app/modules/image-writer.ts +++ b/lib/gui/app/modules/image-writer.ts @@ -25,6 +25,7 @@ import * as settings from '../models/settings'; import * as analytics from '../modules/analytics'; import * as windowProgress from '../os/window-progress'; import { startApiAndSpawnChild } from './api'; +import { terminateScanningServer } from '../app'; /** * @summary Handle a flash error and log it to analytics @@ -150,50 +151,30 @@ async function performWrite( resolve(flashResults); }; - // list api events we want to track and a generic handler to track them - const apiEvents = ['fail', 'done', 'abort', 'skip', 'state']; - const apiEventHandler = (event: any) => { - switch (event.type) { - case 'state': - onProgress(event.payload); - // this is the only event we don't want to terminate the server on - return; - case 'fail': - onFail(event.payload); - break; - case 'done': - onDone(event.payload); - break; - case 'abort': - onAbort(); - break; - case 'skip': - onSkip(); - break; - default: - console.log('Unknown event type', event.type); - return; - } - }; - // Spawn the child process with privileges and wait for the connection to be made - const { emit, terminateServer } = await startApiAndSpawnChild({ - apiEventHandler, - apiEvents, - withPrivileges: true, - }); + const { emit, registerHandler, terminateServer } = + await startApiAndSpawnChild({ + withPrivileges: true, + }); - // TODO: fix this as it's ugly as hell - cancelEmitter = (status: string) => emit(status); + registerHandler('state', onProgress); + registerHandler('fail', onFail); + registerHandler('done', onDone); + registerHandler('abort', onAbort); + registerHandler('skip', onSkip); + + cancelEmitter = (cancelStatus: string) => emit(cancelStatus); // Now that we know we're connected we can instruct the child process to start the write - emit('write', { + const paramaters = { image, destinations: drives, - SourceType: image.SourceType.name, + SourceType: image.SourceType, autoBlockmapping, decompressFirst, - }); + }; + console.log('params', paramaters); + emit('write', paramaters); }); // The process continue in the event handler diff --git a/lib/gui/app/pages/main/Flash.tsx b/lib/gui/app/pages/main/Flash.tsx index 53d20c897d0..25aa42603ba 100644 --- a/lib/gui/app/pages/main/Flash.tsx +++ b/lib/gui/app/pages/main/Flash.tsx @@ -27,7 +27,6 @@ import * as availableDrives from '../../models/available-drives'; import * as flashState from '../../models/flash-state'; import * as selection from '../../models/selection-state'; import * as analytics from '../../modules/analytics'; -import { scanner as driveScanner } from '../../modules/drive-scanner'; import * as imageWriter from '../../modules/image-writer'; import * as notification from '../../os/notification'; import { @@ -95,10 +94,6 @@ async function flashImageToDrive( return ''; } - // Stop scanning drives when flashing - // otherwise Windows throws EPERM - driveScanner.stop(); - const iconPath = path.join('media', 'icon.png'); const basename = path.basename(image.path); try { @@ -110,7 +105,7 @@ async function flashImageToDrive( cancelled, } = flashState.getFlashResults(); if (!skip && !cancelled) { - if (results.devices.successful > 0) { + if (results?.devices?.successful > 0) { notifySuccess(iconPath, basename, drives, results.devices); } else { notifyFailure(iconPath, basename, drives); @@ -129,7 +124,6 @@ async function flashImageToDrive( return errorMessage; } finally { availableDrives.setDrives([]); - driveScanner.start(); } return ''; diff --git a/lib/shared/typings/source-selector.ts b/lib/shared/typings/source-selector.ts index 00c3fa084ac..be2b195b104 100644 --- a/lib/shared/typings/source-selector.ts +++ b/lib/shared/typings/source-selector.ts @@ -2,10 +2,7 @@ import { GPTPartition, MBRPartition } from 'partitioninfo'; import { sourceDestination } from 'etcher-sdk'; import { DrivelistDrive } from '../drive-constraints'; -export type Source = - | typeof sourceDestination.File - | typeof sourceDestination.BlockDevice - | typeof sourceDestination.Http; +export type Source = 'File' | 'BlockDevice' | 'Http'; export interface SourceMetadata extends sourceDestination.Metadata { hasMBR?: boolean; diff --git a/lib/util/api.ts b/lib/util/api.ts index b4312a21ab3..8226b3d2517 100644 --- a/lib/util/api.ts +++ b/lib/util/api.ts @@ -23,6 +23,7 @@ import { WriteOptions } from './types/types'; import { MultiDestinationProgress } from 'etcher-sdk/build/multi-write'; import { write, cleanup } from './child-writer'; import { startScanning } from './scanner'; +import { getSourceMetadata } from './source-metadata'; import { DrivelistDrive } from '../shared/drive-constraints'; import { Dictionary, values } from 'lodash'; @@ -135,6 +136,22 @@ ipc.connectTo(IPC_SERVER_ID, () => { await terminate(SUCCESS); }); + ipc.of[IPC_SERVER_ID].on('sourceMetadata', async (params) => { + log(`sourceMetadata backend ${params}`); + const { selected, SourceType, auth } = JSON.parse(params); + try { + const sourceMatadata = await getSourceMetadata( + selected, + SourceType, + auth, + ); + log(`will response backend ${sourceMatadata}`); + emitSourceMetadata(sourceMatadata); + } catch (error: any) { + emitFail(error); + } + }); + ipc.of[IPC_SERVER_ID].on('scan', async () => { startScanning(); }); @@ -188,4 +205,8 @@ function emitDrives(drives: Dictionary) { emit('drives', JSON.stringify(values(drives))); } -export { emitLog, emitState, emitFail, emitDrives }; +function emitSourceMetadata(sourceMetadata: any) { + emit('sourceMetadata', JSON.stringify(sourceMetadata)); +} + +export { emitLog, emitState, emitFail, emitDrives, emitSourceMetadata }; diff --git a/lib/util/scanner.ts b/lib/util/scanner.ts index 934c8caa3ae..0876a361ae8 100644 --- a/lib/util/scanner.ts +++ b/lib/util/scanner.ts @@ -38,7 +38,6 @@ async function addDrive(drive: Drive) { const drives = getDrives(); drives[preparedDrive.device] = preparedDrive; - console.log('addDrive', drive.raw); setDrives(drives); } diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 8398d92415d..164ad726c7c 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -10,6 +10,7 @@ "hasInstallScript": true, "license": "Apache-2.0", "devDependencies": { + "@babel/register": "^7.22.15", "@balena/lint": "5.4.2", "@balena/sudo-prompt": "9.2.1-workaround-windows-amperstand-in-username-0849e215b947987a643fe5763902aea201255534", "@electron/remote": "^2.0.9", @@ -2450,6 +2451,25 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/register": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.22.15.tgz", + "integrity": "sha512-V3Q3EqoQdn65RCgTLwauZaTfd1ShhwPmbBv+1dkZV/HpCGMKVyn6oFcRlI7RaKqiDQjX2Qd3AuoEguBgdjIKlg==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "find-cache-dir": "^2.0.0", + "make-dir": "^2.1.0", + "pirates": "^4.0.5", + "source-map-support": "^0.5.16" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/regjsgen": { "version": "0.8.0", "dev": true, @@ -6822,6 +6842,12 @@ "dev": true, "license": "MIT" }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, "node_modules/compare-version": { "version": "0.1.2", "dev": true, @@ -9814,6 +9840,93 @@ "dev": true, "license": "MIT" }, + "node_modules/find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-cache-dir/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-cache-dir/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-cache-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-cache-dir/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-cache-dir/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/find-cache-dir/node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/find-up": { "version": "5.0.0", "dev": true, @@ -12497,6 +12610,28 @@ "node": ">=12" } }, + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, "node_modules/make-error": { "version": "1.3.6", "dev": true, @@ -14711,6 +14846,24 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/pkg": { "version": "5.8.1", "dev": true, @@ -21132,6 +21285,19 @@ "@babel/plugin-transform-react-pure-annotations": "^7.22.5" } }, + "@babel/register": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.22.15.tgz", + "integrity": "sha512-V3Q3EqoQdn65RCgTLwauZaTfd1ShhwPmbBv+1dkZV/HpCGMKVyn6oFcRlI7RaKqiDQjX2Qd3AuoEguBgdjIKlg==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "find-cache-dir": "^2.0.0", + "make-dir": "^2.1.0", + "pirates": "^4.0.5", + "source-map-support": "^0.5.16" + } + }, "@babel/regjsgen": { "version": "0.8.0", "dev": true @@ -24275,6 +24441,12 @@ "version": "2.20.3", "dev": true }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, "compare-version": { "version": "0.1.2", "dev": true @@ -26373,6 +26545,71 @@ } } }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + } + } + }, "find-up": { "version": "5.0.0", "dev": true, @@ -28015,6 +28252,24 @@ "@jridgewell/sourcemap-codec": "^1.4.15" } }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true + } + } + }, "make-error": { "version": "1.3.6", "dev": true @@ -29469,6 +29724,18 @@ "version": "2.3.1", "dev": true }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true + }, "pkg": { "version": "5.8.1", "dev": true, diff --git a/package.json b/package.json index d1721896c13..b42765b16ee 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "author": "Balena Ltd. ", "license": "Apache-2.0", "devDependencies": { + "@babel/register": "^7.22.15", "@balena/lint": "5.4.2", "@balena/sudo-prompt": "9.2.1-workaround-windows-amperstand-in-username-0849e215b947987a643fe5763902aea201255534", "@electron/remote": "^2.0.9",