chore: Updated to Theia 1.31.1 (#1662)

Closes #1655
Closes #1656

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
Akos Kitta 2022-11-29 09:39:54 +01:00 committed by GitHub
parent 3ad660927f
commit ac9cce16f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
66 changed files with 3278 additions and 4168 deletions

4
.gitignore vendored
View File

@ -7,7 +7,7 @@ build/
Examples/
!electron/build/
src-gen/
!webpack.config.js
webpack.config.js
gen-webpack.config.js
.DS_Store
# switching from `electron` to `browser` in dev mode.
@ -15,8 +15,6 @@ gen-webpack.config.js
yarn*.log
# For the VS Code extensions used by Theia.
plugins
# the config files for the CLI
arduino-ide-extension/data/cli/config
# the tokens folder for the themes
scripts/themes/tokens
# environment variables

View File

@ -21,27 +21,31 @@
},
"dependencies": {
"@grpc/grpc-js": "^1.6.7",
"@theia/application-package": "1.25.0",
"@theia/core": "1.25.0",
"@theia/editor": "1.25.0",
"@theia/electron": "1.25.0",
"@theia/filesystem": "1.25.0",
"@theia/keymaps": "1.25.0",
"@theia/markers": "1.25.0",
"@theia/monaco": "1.25.0",
"@theia/navigator": "1.25.0",
"@theia/outline-view": "1.25.0",
"@theia/output": "1.25.0",
"@theia/preferences": "1.25.0",
"@theia/search-in-workspace": "1.25.0",
"@theia/terminal": "1.25.0",
"@theia/workspace": "1.25.0",
"@theia/application-package": "1.31.1",
"@theia/core": "1.31.1",
"@theia/debug": "1.31.1",
"@theia/editor": "1.31.1",
"@theia/electron": "1.31.1",
"@theia/filesystem": "1.31.1",
"@theia/keymaps": "1.31.1",
"@theia/markers": "1.31.1",
"@theia/messages": "1.31.1",
"@theia/monaco": "1.31.1",
"@theia/monaco-editor-core": "1.67.2",
"@theia/navigator": "1.31.1",
"@theia/outline-view": "1.31.1",
"@theia/output": "1.31.1",
"@theia/plugin-ext": "1.31.1",
"@theia/preferences": "1.31.1",
"@theia/scm": "1.31.1",
"@theia/search-in-workspace": "1.31.1",
"@theia/terminal": "1.31.1",
"@theia/typehierarchy": "1.31.1",
"@theia/workspace": "1.31.1",
"@tippyjs/react": "^4.2.5",
"@types/atob": "^2.1.2",
"@types/auth0-js": "^9.14.0",
"@types/btoa": "^1.2.3",
"@types/dateformat": "^3.0.1",
"@types/deep-equal": "^1.0.1",
"@types/deepmerge": "^2.2.0",
"@types/glob": "^7.2.0",
"@types/google-protobuf": "^3.7.2",
@ -50,49 +54,48 @@
"@types/lodash.debounce": "^4.0.6",
"@types/ncp": "^2.0.4",
"@types/node-fetch": "^2.5.7",
"@types/p-queue": "^2.3.1",
"@types/ps-tree": "^1.1.0",
"@types/react-select": "^3.0.0",
"@types/react-tabs": "^2.3.2",
"@types/react-virtualized": "^9.21.21",
"@types/temp": "^0.8.34",
"@types/which": "^1.3.1",
"ajv": "^6.5.3",
"arduino-serial-plotter-webapp": "0.2.0",
"async-mutex": "^0.3.0",
"atob": "^2.1.2",
"auth0-js": "^9.14.0",
"btoa": "^1.2.1",
"classnames": "^2.3.1",
"dateformat": "^3.0.3",
"deep-equal": "^2.0.5",
"deepmerge": "2.0.1",
"electron-updater": "^4.6.5",
"fast-safe-stringify": "^2.1.1",
"glob": "^7.1.6",
"google-protobuf": "^3.20.1",
"hash.js": "^1.1.7",
"is-valid-path": "^0.1.1",
"js-yaml": "^3.13.1",
"jwt-decode": "^3.1.2",
"keytar": "7.2.0",
"lodash.debounce": "^4.0.8",
"minimatch": "^3.1.2",
"ncp": "^2.0.0",
"node-fetch": "^2.6.1",
"open": "^8.0.6",
"p-queue": "^5.0.0",
"p-debounce": "^2.1.0",
"p-queue": "^2.4.2",
"ps-tree": "^1.2.0",
"query-string": "^7.0.1",
"react-disable": "^0.1.0",
"react-disable": "^0.1.1",
"react-markdown": "^8.0.0",
"react-select": "^3.0.4",
"react-perfect-scrollbar": "^1.5.8",
"react-select": "^5.6.0",
"react-tabs": "^3.1.2",
"react-virtualized": "^9.22.3",
"react-window": "^1.8.6",
"semver": "^7.3.2",
"string-natural-compare": "^2.0.3",
"temp": "^0.9.1",
"temp-dir": "^2.0.0",
"tree-kill": "^1.2.1",
"upath": "^1.1.2",
"url": "^0.11.0",
"which": "^1.3.1"
},
"devDependencies": {
@ -101,11 +104,10 @@
"@types/chai-string": "^1.4.2",
"@types/mocha": "^5.2.7",
"@types/react-window": "^1.8.5",
"@types/sinon": "^10.0.6",
"@types/sinon-chai": "^3.2.6",
"chai": "^4.2.0",
"chai-string": "^1.5.0",
"decompress": "^4.2.0",
"decompress-tarbz2": "^4.1.1",
"decompress-targz": "^4.1.1",
"decompress-unzip": "^4.0.1",
"download": "^7.1.0",
@ -115,9 +117,6 @@
"moment": "^2.24.0",
"protoc": "^1.0.4",
"shelljs": "^0.8.3",
"sinon": "^12.0.1",
"sinon-chai": "^3.7.0",
"typemoq": "^2.1.0",
"uuid": "^3.2.1",
"yargs": "^11.1.0"
},

View File

@ -3,10 +3,7 @@ import { ContainerModule } from '@theia/core/shared/inversify';
import { WidgetFactory } from '@theia/core/lib/browser/widget-manager';
import { CommandContribution } from '@theia/core/lib/common/command';
import { bindViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
import {
TabBarToolbarContribution,
TabBarToolbarFactory,
} from '@theia/core/lib/browser/shell/tab-bar-toolbar';
import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
import { WebSocketConnectionProvider } from '@theia/core/lib/browser/messaging/ws-connection-provider';
import {
FrontendApplicationContribution,
@ -84,10 +81,7 @@ import { BoardsAutoInstaller } from './boards/boards-auto-installer';
import { ShellLayoutRestorer } from './theia/core/shell-layout-restorer';
import { ListItemRenderer } from './widgets/component-list/list-item-renderer';
import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution';
import {
MonacoThemeJson,
MonacoThemingService,
} from '@theia/monaco/lib/browser/monaco-theming-service';
import {
ArduinoDaemonPath,
ArduinoDaemon,
@ -137,7 +131,6 @@ import { Settings } from './contributions/settings';
import { WorkspaceCommandContribution } from './theia/workspace/workspace-commands';
import { WorkspaceDeleteHandler as TheiaWorkspaceDeleteHandler } from '@theia/workspace/lib/browser/workspace-delete-handler';
import { WorkspaceDeleteHandler } from './theia/workspace/workspace-delete-handler';
import { TabBarToolbar } from './theia/core/tab-bar-toolbar';
import { EditorWidgetFactory as TheiaEditorWidgetFactory } from '@theia/editor/lib/browser/editor-widget-factory';
import { EditorWidgetFactory } from './theia/editor/editor-widget-factory';
import { BurnBootloader } from './contributions/burn-bootloader';
@ -181,8 +174,6 @@ import { EditorCommandContribution } from './theia/editor/editor-command';
import { NavigatorTabBarDecorator as TheiaNavigatorTabBarDecorator } from '@theia/navigator/lib/browser/navigator-tab-bar-decorator';
import { NavigatorTabBarDecorator } from './theia/navigator/navigator-tab-bar-decorator';
import { Debug } from './contributions/debug';
import { DebugSessionManager } from './theia/debug/debug-session-manager';
import { DebugSessionManager as TheiaDebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager';
import { Sketchbook } from './contributions/sketchbook';
import { DebugFrontendApplicationContribution } from './theia/debug/debug-frontend-application-contribution';
import { DebugFrontendApplicationContribution as TheiaDebugFrontendApplicationContribution } from '@theia/debug/lib/browser/debug-frontend-application-contribution';
@ -241,7 +232,6 @@ import { UploadFirmware } from './contributions/upload-firmware';
import {
UploadFirmwareDialog,
UploadFirmwareDialogProps,
UploadFirmwareDialogWidget,
} from './dialogs/firmware-uploader/firmware-uploader-dialog';
import { UploadCertificate } from './contributions/upload-certificate';
@ -258,7 +248,6 @@ import { PlotterFrontendContribution } from './serial/plotter/plotter-frontend-c
import {
UserFieldsDialog,
UserFieldsDialogProps,
UserFieldsDialogWidget,
} from './dialogs/user-fields/user-fields-dialog';
import { nls } from '@theia/core/lib/common';
import { IDEUpdaterCommands } from './ide-updater/ide-updater-commands';
@ -271,7 +260,6 @@ import { IDEUpdaterClientImpl } from './ide-updater/ide-updater-client-impl';
import {
IDEUpdaterDialog,
IDEUpdaterDialogProps,
IDEUpdaterDialogWidget,
} from './dialogs/ide-updater/ide-updater-dialog';
import { ElectronIpcConnectionProvider } from '@theia/core/lib/electron-browser/messaging/electron-ipc-connection-provider';
import { MonitorModel } from './monitor-model';
@ -313,10 +301,6 @@ import { SelectedBoard } from './contributions/selected-board';
import { CheckForIDEUpdates } from './contributions/check-for-ide-updates';
import { OpenBoardsConfig } from './contributions/open-boards-config';
import { SketchFilesTracker } from './contributions/sketch-files-tracker';
import { MonacoThemeServiceIsReady } from './utils/window';
import { Deferred } from '@theia/core/lib/common/promise-util';
import { StatusBarImpl } from './theia/core/status-bar';
import { StatusBarImpl as TheiaStatusBarImpl } from '@theia/core/lib/browser';
import { EditorMenuContribution } from './theia/editor/editor-file';
import { EditorMenuContribution as TheiaEditorMenuContribution } from '@theia/editor/lib/browser/editor-menu';
import { PreferencesEditorWidget as TheiaPreferencesEditorWidget } from '@theia/preferences/lib/browser/views/preference-editor-widget';
@ -337,32 +321,16 @@ import { InterfaceScale } from './contributions/interface-scale';
import { OpenHandler } from '@theia/core/lib/browser/opener-service';
import { NewCloudSketch } from './contributions/new-cloud-sketch';
import { SketchbookCompositeWidget } from './widgets/sketchbook/sketchbook-composite-widget';
const registerArduinoThemes = () => {
const themes: MonacoThemeJson[] = [
{
id: 'arduino-theme',
label: 'Light (Arduino)',
uiTheme: 'vs',
json: require('../../src/browser/data/default.color-theme.json'),
},
{
id: 'arduino-theme-dark',
label: 'Dark (Arduino)',
uiTheme: 'vs-dark',
json: require('../../src/browser/data/dark.color-theme.json'),
},
];
themes.forEach((theme) => MonacoThemingService.register(theme));
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const global = window as any;
const ready = global[MonacoThemeServiceIsReady] as Deferred;
if (ready) {
ready.promise.then(registerArduinoThemes);
} else {
registerArduinoThemes();
}
import { WindowTitleUpdater } from './theia/core/window-title-updater';
import { WindowTitleUpdater as TheiaWindowTitleUpdater } from '@theia/core/lib/browser/window/window-title-updater';
import { ThemeService } from './theia/core/theming';
import { ThemeService as TheiaThemeService } from '@theia/core/lib/browser/theming';
import { MonacoThemingService } from './theia/monaco/monaco-theming-service';
import { MonacoThemingService as TheiaMonacoThemingService } from '@theia/monaco/lib/browser/monaco-theming-service';
import { TypeHierarchyServiceProvider } from './theia/typehierarchy/type-hierarchy-service';
import { TypeHierarchyServiceProvider as TheiaTypeHierarchyServiceProvider } from '@theia/typehierarchy/lib/browser/typehierarchy-service';
import { TypeHierarchyContribution } from './theia/typehierarchy/type-hierarchy-contribution';
import { TypeHierarchyContribution as TheiaTypeHierarchyContribution } from '@theia/typehierarchy/lib/browser/typehierarchy-contribution';
export default new ContainerModule((bind, unbind, isBound, rebind) => {
// Commands and toolbar items
@ -587,14 +555,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
.to(WorkspaceDeleteHandler)
.inSingletonScope();
rebind(TheiaEditorWidgetFactory).to(EditorWidgetFactory).inSingletonScope();
rebind(TabBarToolbarFactory).toFactory(
({ container: parentContainer }) =>
() => {
const container = parentContainer.createChild();
container.bind(TabBarToolbar).toSelf().inSingletonScope();
return container.get(TabBarToolbar);
}
);
bind(OutputChannelManager).toSelf().inSingletonScope();
rebind(TheiaOutputChannelManager).toService(OutputChannelManager);
bind(OutputChannelRegistryMainImpl).toSelf().inTransientScope();
@ -838,9 +798,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(AboutDialog).toSelf().inSingletonScope();
rebind(TheiaAboutDialog).toService(AboutDialog);
// To avoid running `Save All` when there are no dirty editors before starting the debug session.
bind(DebugSessionManager).toSelf().inSingletonScope();
rebind(TheiaDebugSessionManager).toService(DebugSessionManager);
// To remove the `Run` menu item from the application menu.
bind(DebugFrontendApplicationContribution).toSelf().inSingletonScope();
rebind(TheiaDebugFrontendApplicationContribution).toService(
@ -854,10 +811,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(WidgetManager).toSelf().inSingletonScope();
rebind(TheiaWidgetManager).toService(WidgetManager);
// To avoid running a status bar update on every single `keypress` event from the editor.
bind(StatusBarImpl).toSelf().inSingletonScope();
rebind(TheiaStatusBarImpl).toService(StatusBarImpl);
// Debounced update for the tab-bar toolbar when typing in the editor.
bind(DockPanelRenderer).toSelf();
rebind(TheiaDockPanelRenderer).toService(DockPanelRenderer);
@ -942,12 +895,11 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(LocalCacheFsProvider).toSelf().inSingletonScope();
bind(FileServiceContribution).toService(LocalCacheFsProvider);
bind(CloudSketchbookCompositeWidget).toSelf();
bind<WidgetFactory>(WidgetFactory).toDynamicValue((ctx) => ({
bind(WidgetFactory).toDynamicValue((ctx) => ({
id: 'cloud-sketchbook-composite-widget',
createWidget: () => ctx.container.get(CloudSketchbookCompositeWidget),
}));
bind(UploadFirmwareDialogWidget).toSelf().inSingletonScope();
bind(UploadFirmwareDialog).toSelf().inSingletonScope();
bind(UploadFirmwareDialogProps).toConstantValue({
title: 'UploadFirmware',
@ -958,13 +910,11 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
title: 'UploadCertificate',
});
bind(IDEUpdaterDialogWidget).toSelf().inSingletonScope();
bind(IDEUpdaterDialog).toSelf().inSingletonScope();
bind(IDEUpdaterDialogProps).toConstantValue({
title: 'IDEUpdater',
});
bind(UserFieldsDialogWidget).toSelf().inSingletonScope();
bind(UserFieldsDialog).toSelf().inSingletonScope();
bind(UserFieldsDialogProps).toConstantValue({
title: 'UserFields',
@ -991,4 +941,23 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
rebind(TheiaHostedPluginSupport).toService(HostedPluginSupport);
bind(HostedPluginEvents).toSelf().inSingletonScope();
bind(FrontendApplicationContribution).toService(HostedPluginEvents);
// custom window titles
bind(WindowTitleUpdater).toSelf().inSingletonScope();
rebind(TheiaWindowTitleUpdater).toService(WindowTitleUpdater);
// register Arduino themes
bind(ThemeService).toSelf().inSingletonScope();
rebind(TheiaThemeService).toService(ThemeService);
bind(MonacoThemingService).toSelf().inSingletonScope();
rebind(TheiaMonacoThemingService).toService(MonacoThemingService);
// disable type-hierarchy support
// https://github.com/eclipse-theia/theia/commit/16c88a584bac37f5cf3cc5eb92ffdaa541bda5be
bind(TypeHierarchyServiceProvider).toSelf().inSingletonScope();
rebind(TheiaTypeHierarchyServiceProvider).toService(
TypeHierarchyServiceProvider
);
bind(TypeHierarchyContribution).toSelf().inSingletonScope();
rebind(TheiaTypeHierarchyContribution).toService(TypeHierarchyContribution);
});

View File

@ -235,7 +235,7 @@ export class SketchControl extends SketchContribution {
});
registry.registerKeybinding({
command: CommonCommands.PREVIOUS_TAB.id,
keybinding: 'CtrlCmd+Alt+Left', // TODO: check why electron does not show the keybindings in the UI.
keybinding: 'CtrlCmd+Alt+Left',
});
registry.registerKeybinding({
command: CommonCommands.NEXT_TAB.id,

View File

@ -1,4 +1,4 @@
import { URI as Uri } from 'vscode-uri';
import { URI as Uri } from '@theia/core/shared/vscode-uri';
import URI from '@theia/core/lib/common/uri';
import { toPosixPath, parentPosix, posix } from './create-paths';
import { Create } from './typings';

View File

@ -5,10 +5,8 @@ import {
postConstruct,
} from '@theia/core/shared/inversify';
import { DialogProps } from '@theia/core/lib/browser/dialogs';
import { AbstractDialog } from '../../theia/dialogs/dialogs';
import { Widget } from '@theia/core/shared/@phosphor/widgets';
import { ReactDialog } from '../../theia/dialogs/dialogs';
import { Message } from '@theia/core/shared/@phosphor/messaging';
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
import {
AvailableBoard,
BoardsServiceProvider,
@ -23,26 +21,30 @@ import { Port } from '../../../common/protocol';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
@injectable()
export class UploadFirmwareDialogWidget extends ReactWidget {
export class UploadFirmwareDialogProps extends DialogProps {}
@injectable()
export class UploadFirmwareDialog extends ReactDialog<void> {
@inject(BoardsServiceProvider)
protected readonly boardsServiceClient: BoardsServiceProvider;
private readonly boardsServiceClient: BoardsServiceProvider;
@inject(ArduinoFirmwareUploader)
protected readonly arduinoFirmwareUploader: ArduinoFirmwareUploader;
private readonly arduinoFirmwareUploader: ArduinoFirmwareUploader;
@inject(FrontendApplicationStateService)
private readonly appStatusService: FrontendApplicationStateService;
protected updatableFqbns: string[] = [];
protected availableBoards: AvailableBoard[] = [];
protected isOpen = new Object();
private updatableFqbns: string[] = [];
private availableBoards: AvailableBoard[] = [];
private isOpen = new Object();
private busy = false;
public busyCallback = (busy: boolean) => {
return;
};
constructor() {
super();
constructor(
@inject(UploadFirmwareDialogProps)
protected override readonly props: UploadFirmwareDialogProps
) {
super({ title: UploadFirmware.Commands.OPEN.label || '' });
this.node.id = 'firmware-uploader-dialog-container';
this.contentNode.classList.add('firmware-uploader-dialog');
this.acceptButton = undefined;
}
@postConstruct()
@ -59,79 +61,34 @@ export class UploadFirmwareDialogWidget extends ReactWidget {
});
}
protected flashFirmware(firmware: FirmwareInfo, port: Port): Promise<any> {
this.busyCallback(true);
return this.arduinoFirmwareUploader
.flash(firmware, port)
.finally(() => this.busyCallback(false));
}
protected override onCloseRequest(msg: Message): void {
super.onCloseRequest(msg);
this.isOpen = new Object();
}
protected render(): React.ReactNode {
return (
<form>
<FirmwareUploaderComponent
availableBoards={this.availableBoards}
firmwareUploader={this.arduinoFirmwareUploader}
flashFirmware={this.flashFirmware.bind(this)}
updatableFqbns={this.updatableFqbns}
isOpen={this.isOpen}
/>
</form>
);
}
}
@injectable()
export class UploadFirmwareDialogProps extends DialogProps {}
@injectable()
export class UploadFirmwareDialog extends AbstractDialog<void> {
@inject(UploadFirmwareDialogWidget)
protected readonly widget: UploadFirmwareDialogWidget;
private busy = false;
constructor(
@inject(UploadFirmwareDialogProps)
protected override readonly props: UploadFirmwareDialogProps
) {
super({ title: UploadFirmware.Commands.OPEN.label || '' });
this.node.id = 'firmware-uploader-dialog-container';
this.contentNode.classList.add('firmware-uploader-dialog');
this.acceptButton = undefined;
}
get value(): void {
return;
}
protected override render(): React.ReactNode {
return (
<div>
<form>
<FirmwareUploaderComponent
availableBoards={this.availableBoards}
firmwareUploader={this.arduinoFirmwareUploader}
flashFirmware={this.flashFirmware.bind(this)}
updatableFqbns={this.updatableFqbns}
isOpen={this.isOpen}
/>
</form>
</div>
);
}
protected override onAfterAttach(msg: Message): void {
if (this.widget.isAttached) {
Widget.detach(this.widget);
}
Widget.attach(this.widget, this.contentNode);
const firstButton = this.widget.node.querySelector('button');
const firstButton = this.node.querySelector('button');
firstButton?.focus();
this.widget.busyCallback = this.busyCallback.bind(this);
super.onAfterAttach(msg);
this.update();
}
protected override onUpdateRequest(msg: Message): void {
super.onUpdateRequest(msg);
this.widget.update();
}
protected override onActivateRequest(msg: Message): void {
super.onActivateRequest(msg);
this.widget.activate();
}
// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
protected override handleEnter(event: KeyboardEvent): boolean | void {
return false;
}
@ -140,11 +97,11 @@ export class UploadFirmwareDialog extends AbstractDialog<void> {
if (this.busy) {
return;
}
this.widget.close();
super.close();
this.isOpen = new Object();
}
busyCallback(busy: boolean): void {
private busyCallback(busy: boolean): void {
this.busy = busy;
if (busy) {
this.closeCrossNode.classList.add('disabled');
@ -152,4 +109,11 @@ export class UploadFirmwareDialog extends AbstractDialog<void> {
this.closeCrossNode.classList.remove('disabled');
}
}
private flashFirmware(firmware: FirmwareInfo, port: Port): Promise<any> {
this.busyCallback(true);
return this.arduinoFirmwareUploader
.flash(firmware, port)
.finally(() => this.busyCallback(false));
}
}

View File

@ -1,7 +1,6 @@
import { nls } from '@theia/core/lib/common';
import { shell } from 'electron';
import { shell } from '@theia/core/electron-shared/@electron/remote';
import * as React from '@theia/core/shared/react';
import * as ReactDOM from '@theia/core/shared/react-dom';
import ReactMarkdown from 'react-markdown';
import { ProgressInfo, UpdateInfo } from '../../../common/protocol/ide-updater';
import ProgressBar from '../../components/ProgressBar';
@ -28,32 +27,19 @@ export const IDEUpdaterComponent = ({
},
}: IDEUpdaterComponentProps): React.ReactElement => {
const { version, releaseNotes } = updateInfo;
const changelogDivRef =
React.useRef() as React.MutableRefObject<HTMLDivElement>;
const [changelog, setChangelog] = React.useState<string>('');
React.useEffect(() => {
if (!!releaseNotes && changelogDivRef.current) {
let changelog: string;
if (typeof releaseNotes === 'string') changelog = releaseNotes;
else
changelog = releaseNotes.reduce((acc, item) => {
return item.note ? (acc += `${item.note}\n\n`) : acc;
}, '');
ReactDOM.render(
<ReactMarkdown
components={{
a: ({ href, children, ...props }) => (
<a onClick={() => href && shell.openExternal(href)} {...props}>
{children}
</a>
),
}}
>
{changelog}
</ReactMarkdown>,
changelogDivRef.current
if (releaseNotes) {
setChangelog(
typeof releaseNotes === 'string'
? releaseNotes
: releaseNotes.reduce(
(acc, item) => (item.note ? (acc += `${item.note}\n\n`) : acc),
''
)
);
}
}, [updateInfo]);
}, [releaseNotes, changelog]);
const DownloadCompleted: () => React.ReactElement = () => (
<div className="ide-updater-dialog--downloaded">
@ -106,9 +92,24 @@ export const IDEUpdaterComponent = ({
version
)}
</div>
{releaseNotes && (
{changelog && (
<div className="dialogRow changelog-container">
<div className="changelog" ref={changelogDivRef} />
<div className="changelog">
<ReactMarkdown
components={{
a: ({ href, children, ...props }) => (
<a
onClick={() => href && shell.openExternal(href)}
{...props}
>
{children}
</a>
),
}}
>
{changelog}
</ReactMarkdown>
</div>
</div>
)}
</div>

View File

@ -5,10 +5,8 @@ import {
postConstruct,
} from '@theia/core/shared/inversify';
import { DialogProps } from '@theia/core/lib/browser/dialogs';
import { AbstractDialog } from '../../theia/dialogs/dialogs';
import { Widget } from '@theia/core/shared/@phosphor/widgets';
import { Message } from '@theia/core/shared/@phosphor/messaging';
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
import { ReactDialog } from '../../theia/dialogs/dialogs';
import { nls } from '@theia/core';
import { IDEUpdaterComponent, UpdateProgress } from './ide-updater-component';
import {
@ -22,47 +20,11 @@ import { WindowService } from '@theia/core/lib/browser/window/window-service';
const DOWNLOAD_PAGE_URL = 'https://www.arduino.cc/en/software';
@injectable()
export class IDEUpdaterDialogWidget extends ReactWidget {
private _updateInfo: UpdateInfo;
private _updateProgress: UpdateProgress = {};
setUpdateInfo(updateInfo: UpdateInfo): void {
this._updateInfo = updateInfo;
this.update();
}
mergeUpdateProgress(updateProgress: UpdateProgress): void {
this._updateProgress = { ...this._updateProgress, ...updateProgress };
this.update();
}
get updateInfo(): UpdateInfo {
return this._updateInfo;
}
get updateProgress(): UpdateProgress {
return this._updateProgress;
}
protected render(): React.ReactNode {
return !!this._updateInfo ? (
<IDEUpdaterComponent
updateInfo={this._updateInfo}
updateProgress={this._updateProgress}
/>
) : null;
}
}
@injectable()
export class IDEUpdaterDialogProps extends DialogProps {}
@injectable()
export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
@inject(IDEUpdaterDialogWidget)
private readonly widget: IDEUpdaterDialogWidget;
export class IDEUpdaterDialog extends ReactDialog<UpdateInfo | undefined> {
@inject(IDEUpdater)
private readonly updater: IDEUpdater;
@ -75,6 +37,9 @@ export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
@inject(WindowService)
private readonly windowService: WindowService;
private _updateInfo: UpdateInfo | undefined;
private _updateProgress: UpdateProgress = {};
constructor(
@inject(IDEUpdaterDialogProps)
protected override readonly props: IDEUpdaterDialogProps
@ -94,26 +59,34 @@ export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
protected init(): void {
this.updaterClient.onUpdaterDidFail((error) => {
this.appendErrorButtons();
this.widget.mergeUpdateProgress({ error });
this.mergeUpdateProgress({ error });
});
this.updaterClient.onDownloadProgressDidChange((progressInfo) => {
this.widget.mergeUpdateProgress({ progressInfo });
this.mergeUpdateProgress({ progressInfo });
});
this.updaterClient.onDownloadDidFinish(() => {
this.appendInstallButtons();
this.widget.mergeUpdateProgress({ downloadFinished: true });
this.mergeUpdateProgress({ downloadFinished: true });
});
}
get value(): UpdateInfo {
return this.widget.updateInfo;
protected render(): React.ReactNode {
return (
this.updateInfo && (
<IDEUpdaterComponent
updateInfo={this.updateInfo}
updateProgress={this.updateProgress}
/>
)
);
}
get value(): UpdateInfo | undefined {
return this.updateInfo;
}
protected override onAfterAttach(msg: Message): void {
if (this.widget.isAttached) {
Widget.detach(this.widget);
}
Widget.attach(this.widget, this.contentNode);
this.update();
this.appendInitialButtons();
super.onAfterAttach(msg);
}
@ -196,15 +169,19 @@ export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
}
private skipVersion(): void {
if (!this.updateInfo) {
console.warn(`Nothing to skip. No update info is available`);
return;
}
this.localStorageService.setData<string>(
SKIP_IDE_VERSION,
this.widget.updateInfo.version
this.updateInfo.version
);
this.close();
}
private startDownload(): void {
this.widget.mergeUpdateProgress({
this.mergeUpdateProgress({
downloadStarted: true,
});
this.clearButtons();
@ -216,31 +193,48 @@ export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
this.close();
}
private set updateInfo(updateInfo: UpdateInfo | undefined) {
this._updateInfo = updateInfo;
this.update();
}
private get updateInfo(): UpdateInfo | undefined {
return this._updateInfo;
}
private get updateProgress(): UpdateProgress {
return this._updateProgress;
}
private mergeUpdateProgress(updateProgress: UpdateProgress): void {
this._updateProgress = { ...this._updateProgress, ...updateProgress };
this.update();
}
override async open(
data: UpdateInfo | undefined = undefined
): Promise<UpdateInfo | undefined> {
if (data && data.version) {
this.widget.mergeUpdateProgress({
this.mergeUpdateProgress({
progressInfo: undefined,
downloadStarted: false,
downloadFinished: false,
error: undefined,
});
this.widget.setUpdateInfo(data);
this.updateInfo = data;
return super.open();
}
}
protected override onActivateRequest(msg: Message): void {
super.onActivateRequest(msg);
this.widget.activate();
this.update();
}
override close(): void {
this.widget.dispose();
if (
this.widget.updateProgress?.downloadStarted &&
!this.widget.updateProgress?.downloadFinished
this.updateProgress?.downloadStarted &&
!this.updateProgress?.downloadFinished
) {
this.updater.stopDownload();
}

View File

@ -218,16 +218,14 @@ export class SettingsComponent extends React.Component<
<div className="flex-line">
<select
className="theia-select"
value={ThemeService.get().getCurrentTheme().label}
value={this.props.themeService.getCurrentTheme().label}
onChange={this.themeDidChange}
>
{ThemeService.get()
.getThemes()
.map(({ id, label }) => (
<option key={id} value={label}>
{label}
</option>
))}
{this.props.themeService.getThemes().map(({ id, label }) => (
<option key={id} value={label}>
{label}
</option>
))}
</select>
</div>
<div className="flex-line">
@ -612,11 +610,11 @@ export class SettingsComponent extends React.Component<
event: React.ChangeEvent<HTMLSelectElement>
): void => {
const { selectedIndex } = event.target.options;
const theme = ThemeService.get().getThemes()[selectedIndex];
const theme = this.props.themeService.getThemes()[selectedIndex];
if (theme) {
this.setState({ themeId: theme.id });
if (ThemeService.get().getCurrentTheme().id !== theme.id) {
ThemeService.get().setCurrentTheme(theme.id);
if (this.props.themeService.getCurrentTheme().id !== theme.id) {
this.props.themeService.setCurrentTheme(theme.id);
}
}
};
@ -755,6 +753,7 @@ export namespace SettingsComponent {
readonly fileDialogService: FileDialogService;
readonly windowService: WindowService;
readonly localizationProvider: AsyncLocalizationProvider;
readonly themeService: ThemeService;
}
export type State = Settings & {
rawAdditionalUrlsValue: string;

View File

@ -35,6 +35,9 @@ export class SettingsWidget extends ReactWidget {
@inject(AsyncLocalizationProvider)
protected readonly localizationProvider: AsyncLocalizationProvider;
@inject(ThemeService)
private readonly themeService: ThemeService;
protected render(): React.ReactNode {
return (
<SettingsComponent
@ -43,6 +46,7 @@ export class SettingsWidget extends ReactWidget {
fileDialogService={this.fileDialogService}
windowService={this.windowService}
localizationProvider={this.localizationProvider}
themeService={this.themeService}
/>
);
}
@ -59,6 +63,9 @@ export class SettingsDialog extends AbstractDialog<Promise<Settings>> {
@inject(SettingsWidget)
protected readonly widget: SettingsWidget;
@inject(ThemeService)
private readonly themeService: ThemeService;
constructor(
@inject(SettingsDialogProps)
protected override readonly props: SettingsDialogProps
@ -121,11 +128,11 @@ export class SettingsDialog extends AbstractDialog<Promise<Settings>> {
}
override async open(): Promise<Promise<Settings> | undefined> {
const themeIdBeforeOpen = ThemeService.get().getCurrentTheme().id;
const themeIdBeforeOpen = this.themeService.getCurrentTheme().id;
const result = await super.open();
if (!result) {
if (ThemeService.get().getCurrentTheme().id !== themeIdBeforeOpen) {
ThemeService.get().setCurrentTheme(themeIdBeforeOpen);
if (this.themeService.getCurrentTheme().id !== themeIdBeforeOpen) {
this.themeService.setCurrentTheme(themeIdBeforeOpen);
}
}
return result;

View File

@ -5,7 +5,7 @@ import {
} from '@theia/core/shared/inversify';
import URI from '@theia/core/lib/common/uri';
import { Emitter } from '@theia/core/lib/common/event';
import { Deferred, timeout } from '@theia/core/lib/common/promise-util';
import { Deferred } from '@theia/core/lib/common/promise-util';
import { deepClone } from '@theia/core/lib/common/objects';
import { FileService } from '@theia/filesystem/lib/browser/file-service';
import { ThemeService } from '@theia/core/lib/browser/theming';
@ -25,6 +25,8 @@ import {
LanguageInfo,
} from '@theia/core/lib/common/i18n/localization';
import { ElectronCommands } from '@theia/core/lib/electron-browser/menu/electron-menu-contribution';
import { DefaultTheme } from '@theia/application-package/lib/application-props';
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
export const EDITOR_SETTING = 'editor';
export const FONT_SIZE_SETTING = `${EDITOR_SETTING}.fontSize`;
@ -101,6 +103,9 @@ export class SettingsService {
@inject(CommandService)
protected commandService: CommandService;
@inject(ThemeService)
private readonly themeService: ThemeService;
protected readonly onDidChangeEmitter = new Emitter<Readonly<Settings>>();
readonly onDidChange = this.onDidChangeEmitter.event;
protected readonly onDidResetEmitter = new Emitter<Readonly<Settings>>();
@ -141,10 +146,9 @@ export class SettingsService {
this.preferenceService.get<number>(FONT_SIZE_SETTING, 12),
this.preferenceService.get<string>(
'workbench.colorTheme',
window.matchMedia &&
window.matchMedia('(prefers-color-scheme: dark)').matches
? 'arduino-theme-dark'
: 'arduino-theme'
DefaultTheme.defaultForOSTheme(
FrontendApplicationConfigProvider.get().defaultTheme
)
),
this.preferenceService.get<Settings.AutoSave>(
AUTO_SAVE_SETTING,
@ -231,11 +235,7 @@ export class SettingsService {
'Invalid editor font size. It must be a positive integer.'
);
}
if (
!ThemeService.get()
.getThemes()
.find(({ id }) => id === themeId)
) {
if (!this.themeService.getThemes().find(({ id }) => id === themeId)) {
return nls.localize(
'arduino/preferences/invalid.theme',
'Invalid theme.'
@ -252,7 +252,6 @@ export class SettingsService {
private async savePreference(name: string, value: unknown): Promise<void> {
await this.preferenceService.set(name, value, PreferenceScope.User);
await timeout(5);
}
async save(): Promise<string | true> {
@ -283,19 +282,21 @@ export class SettingsService {
(config as any).network = network;
(config as any).locale = currentLanguage;
await this.savePreference('editor.fontSize', editorFontSize);
await this.savePreference('workbench.colorTheme', themeId);
await this.savePreference(AUTO_SAVE_SETTING, autoSave);
await this.savePreference('editor.quickSuggestions', quickSuggestions);
await this.savePreference(AUTO_SCALE_SETTING, autoScaleInterface);
await this.savePreference(ZOOM_LEVEL_SETTING, interfaceScale);
await this.savePreference(ZOOM_LEVEL_SETTING, interfaceScale);
await this.savePreference(COMPILE_VERBOSE_SETTING, verboseOnCompile);
await this.savePreference(COMPILE_WARNINGS_SETTING, compilerWarnings);
await this.savePreference(UPLOAD_VERBOSE_SETTING, verboseOnUpload);
await this.savePreference(UPLOAD_VERIFY_SETTING, verifyAfterUpload);
await this.savePreference(SHOW_ALL_FILES_SETTING, sketchbookShowAllFiles);
await this.configService.setConfiguration(config);
await Promise.all([
this.savePreference('editor.fontSize', editorFontSize),
this.savePreference('workbench.colorTheme', themeId),
this.savePreference(AUTO_SAVE_SETTING, autoSave),
this.savePreference('editor.quickSuggestions', quickSuggestions),
this.savePreference(AUTO_SCALE_SETTING, autoScaleInterface),
this.savePreference(ZOOM_LEVEL_SETTING, interfaceScale),
this.savePreference(ZOOM_LEVEL_SETTING, interfaceScale),
this.savePreference(COMPILE_VERBOSE_SETTING, verboseOnCompile),
this.savePreference(COMPILE_WARNINGS_SETTING, compilerWarnings),
this.savePreference(UPLOAD_VERBOSE_SETTING, verboseOnUpload),
this.savePreference(UPLOAD_VERIFY_SETTING, verifyAfterUpload),
this.savePreference(SHOW_ALL_FILES_SETTING, sketchbookShowAllFiles),
this.configService.setConfiguration(config),
]);
this.onDidChangeEmitter.fire(this._settings);
// after saving all the settings, if we need to change the language we need to perform a reload

View File

@ -1,63 +1,18 @@
import * as React from '@theia/core/shared/react';
import { inject, injectable } from '@theia/core/shared/inversify';
import {
AbstractDialog,
DialogProps,
ReactWidget,
} from '@theia/core/lib/browser';
import { Widget } from '@theia/core/shared/@phosphor/widgets';
import { DialogProps } from '@theia/core/lib/browser/dialogs';
import { Message } from '@theia/core/shared/@phosphor/messaging';
import { UploadSketch } from '../../contributions/upload-sketch';
import { UserFieldsComponent } from './user-fields-component';
import { BoardUserField } from '../../../common/protocol';
@injectable()
export class UserFieldsDialogWidget extends ReactWidget {
private _currentUserFields: BoardUserField[] = [];
constructor(private cancel: () => void, private accept: () => Promise<void>) {
super();
}
set currentUserFields(userFields: BoardUserField[]) {
this.setUserFields(userFields);
}
get currentUserFields(): BoardUserField[] {
return this._currentUserFields;
}
resetUserFieldsValue(): void {
this._currentUserFields = this._currentUserFields.map((field) => {
field.value = '';
return field;
});
}
private setUserFields(userFields: BoardUserField[]): void {
this._currentUserFields = userFields;
}
protected render(): React.ReactNode {
return (
<form>
<UserFieldsComponent
initialBoardUserFields={this._currentUserFields}
updateUserFields={this.setUserFields.bind(this)}
cancel={this.cancel}
accept={this.accept}
/>
</form>
);
}
}
import { ReactDialog } from '../../theia/dialogs/dialogs';
@injectable()
export class UserFieldsDialogProps extends DialogProps {}
@injectable()
export class UserFieldsDialog extends AbstractDialog<BoardUserField[]> {
protected readonly widget: UserFieldsDialogWidget;
export class UserFieldsDialog extends ReactDialog<BoardUserField[]> {
private _currentUserFields: BoardUserField[] = [];
constructor(
@inject(UserFieldsDialogProps)
@ -69,39 +24,36 @@ export class UserFieldsDialog extends AbstractDialog<BoardUserField[]> {
this.titleNode.classList.add('user-fields-dialog-title');
this.contentNode.classList.add('user-fields-dialog-content');
this.acceptButton = undefined;
this.widget = new UserFieldsDialogWidget(
this.close.bind(this),
this.accept.bind(this)
);
}
set value(userFields: BoardUserField[]) {
this.widget.currentUserFields = userFields;
}
get value(): BoardUserField[] {
return this.widget.currentUserFields;
return this._currentUserFields;
}
set value(userFields: BoardUserField[]) {
this._currentUserFields = userFields;
}
protected override render(): React.ReactNode {
return (
<div>
<form>
<UserFieldsComponent
initialBoardUserFields={this.value}
updateUserFields={this.doUpdateUserFields}
cancel={this.doCancel}
accept={this.doAccept}
/>
</form>
</div>
);
}
protected override onAfterAttach(msg: Message): void {
if (this.widget.isAttached) {
Widget.detach(this.widget);
}
Widget.attach(this.widget, this.contentNode);
super.onAfterAttach(msg);
this.update();
}
protected override onUpdateRequest(msg: Message): void {
super.onUpdateRequest(msg);
this.widget.update();
}
protected override onActivateRequest(msg: Message): void {
super.onActivateRequest(msg);
this.widget.activate();
}
protected override async accept(): Promise<void> {
// If the user presses enter and at least
// a field is empty don't accept the input
@ -114,8 +66,21 @@ export class UserFieldsDialog extends AbstractDialog<BoardUserField[]> {
}
override close(): void {
this.widget.resetUserFieldsValue();
this.widget.close();
this.resetUserFieldsValue();
super.close();
}
private resetUserFieldsValue(): void {
this.value = this.value.map((field) => {
field.value = '';
return field;
});
}
private readonly doCancel: () => void = () => this.close();
private readonly doAccept: () => Promise<void> = () => this.accept();
private readonly doUpdateUserFields: (userFields: BoardUserField[]) => void =
(userFields: BoardUserField[]) => {
this.value = userFields;
};
}

View File

@ -0,0 +1,6 @@
<!--Copyright (c) Microsoft Corporation. All rights reserved.-->
<!--Copyright (C) 2020 TypeFox and others.-->
<!--Copied from https://raw.githubusercontent.com/microsoft/vscode-icons/9c90ce81b1f3c309000b80da0763aa09975a85f0/icons/dark/loading.svg -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 0.75C8.17188 0.75 8.33333 0.783854 8.48438 0.851562C8.63542 0.914062 8.76823 1.0026 8.88281 1.11719C8.9974 1.23177 9.08594 1.36458 9.14844 1.51562C9.21615 1.66667 9.25 1.82812 9.25 2C9.25 2.17188 9.21615 2.33333 9.14844 2.48438C9.08594 2.63542 8.9974 2.76823 8.88281 2.88281C8.76823 2.9974 8.63542 3.08854 8.48438 3.15625C8.33333 3.21875 8.17188 3.25 8 3.25C7.82812 3.25 7.66667 3.21875 7.51562 3.15625C7.36458 3.08854 7.23177 2.9974 7.11719 2.88281C7.0026 2.76823 6.91146 2.63542 6.84375 2.48438C6.78125 2.33333 6.75 2.17188 6.75 2C6.75 1.82812 6.78125 1.66667 6.84375 1.51562C6.91146 1.36458 7.0026 1.23177 7.11719 1.11719C7.23177 1.0026 7.36458 0.914062 7.51562 0.851562C7.66667 0.783854 7.82812 0.75 8 0.75ZM2.63281 3.75781C2.63281 3.60156 2.66146 3.45573 2.71875 3.32031C2.77604 3.1849 2.85417 3.06771 2.95312 2.96875C3.05729 2.86458 3.17708 2.78385 3.3125 2.72656C3.45312 2.66406 3.60156 2.63281 3.75781 2.63281C3.91406 2.63281 4.0599 2.66406 4.19531 2.72656C4.33073 2.78385 4.44792 2.86458 4.54688 2.96875C4.65104 3.06771 4.73177 3.1849 4.78906 3.32031C4.85156 3.45573 4.88281 3.60156 4.88281 3.75781C4.88281 3.91406 4.85156 4.0625 4.78906 4.20312C4.73177 4.33854 4.65104 4.45833 4.54688 4.5625C4.44792 4.66146 4.33073 4.73958 4.19531 4.79688C4.0599 4.85417 3.91406 4.88281 3.75781 4.88281C3.60156 4.88281 3.45312 4.85417 3.3125 4.79688C3.17708 4.73958 3.05729 4.66146 2.95312 4.5625C2.85417 4.45833 2.77604 4.33854 2.71875 4.20312C2.66146 4.0625 2.63281 3.91406 2.63281 3.75781ZM2 7C2.14062 7 2.27083 7.02604 2.39062 7.07812C2.51042 7.13021 2.61458 7.20312 2.70312 7.29688C2.79688 7.38542 2.86979 7.48958 2.92188 7.60938C2.97396 7.72917 3 7.85938 3 8C3 8.14062 2.97396 8.27083 2.92188 8.39062C2.86979 8.51042 2.79688 8.61719 2.70312 8.71094C2.61458 8.79948 2.51042 8.86979 2.39062 8.92188C2.27083 8.97396 2.14062 9 2 9C1.85938 9 1.72917 8.97396 1.60938 8.92188C1.48958 8.86979 1.38281 8.79948 1.28906 8.71094C1.20052 8.61719 1.13021 8.51042 1.07812 8.39062C1.02604 8.27083 1 8.14062 1 8C1 7.85938 1.02604 7.72917 1.07812 7.60938C1.13021 7.48958 1.20052 7.38542 1.28906 7.29688C1.38281 7.20312 1.48958 7.13021 1.60938 7.07812C1.72917 7.02604 1.85938 7 2 7ZM2.88281 12.2422C2.88281 12.1224 2.90625 12.0104 2.95312 11.9062C3 11.7969 3.0625 11.7031 3.14062 11.625C3.21875 11.5469 3.3099 11.4844 3.41406 11.4375C3.52344 11.3906 3.63802 11.3672 3.75781 11.3672C3.8776 11.3672 3.98958 11.3906 4.09375 11.4375C4.20312 11.4844 4.29688 11.5469 4.375 11.625C4.45312 11.7031 4.51562 11.7969 4.5625 11.9062C4.60938 12.0104 4.63281 12.1224 4.63281 12.2422C4.63281 12.362 4.60938 12.4766 4.5625 12.5859C4.51562 12.6901 4.45312 12.7812 4.375 12.8594C4.29688 12.9375 4.20312 13 4.09375 13.0469C3.98958 13.0938 3.8776 13.1172 3.75781 13.1172C3.63802 13.1172 3.52344 13.0938 3.41406 13.0469C3.3099 13 3.21875 12.9375 3.14062 12.8594C3.0625 12.7812 3 12.6901 2.95312 12.5859C2.90625 12.4766 2.88281 12.362 2.88281 12.2422ZM8 13.25C8.20833 13.25 8.38542 13.3229 8.53125 13.4688C8.67708 13.6146 8.75 13.7917 8.75 14C8.75 14.2083 8.67708 14.3854 8.53125 14.5312C8.38542 14.6771 8.20833 14.75 8 14.75C7.79167 14.75 7.61458 14.6771 7.46875 14.5312C7.32292 14.3854 7.25 14.2083 7.25 14C7.25 13.7917 7.32292 13.6146 7.46875 13.4688C7.61458 13.3229 7.79167 13.25 8 13.25ZM11.6172 12.2422C11.6172 12.0651 11.6771 11.9167 11.7969 11.7969C11.9167 11.6771 12.0651 11.6172 12.2422 11.6172C12.4193 11.6172 12.5677 11.6771 12.6875 11.7969C12.8073 11.9167 12.8672 12.0651 12.8672 12.2422C12.8672 12.4193 12.8073 12.5677 12.6875 12.6875C12.5677 12.8073 12.4193 12.8672 12.2422 12.8672C12.0651 12.8672 11.9167 12.8073 11.7969 12.6875C11.6771 12.5677 11.6172 12.4193 11.6172 12.2422ZM14 7.5C14.1354 7.5 14.2526 7.54948 14.3516 7.64844C14.4505 7.7474 14.5 7.86458 14.5 8C14.5 8.13542 14.4505 8.2526 14.3516 8.35156C14.2526 8.45052 14.1354 8.5 14 8.5C13.8646 8.5 13.7474 8.45052 13.6484 8.35156C13.5495 8.2526 13.5 8.13542 13.5 8C13.5 7.86458 13.5495 7.7474 13.6484 7.64844C13.7474 7.54948 13.8646 7.5 14 7.5ZM12.2422 2.38281C12.4297 2.38281 12.6068 2.41927 12.7734 2.49219C12.9401 2.5651 13.0859 2.66406 13.2109 2.78906C13.3359 2.91406 13.4349 3.0599 13.5078 3.22656C13.5807 3.39323 13.6172 3.57031 13.6172 3.75781C13.6172 3.94531 13.5807 4.1224 13.5078 4.28906C13.4349 4.45573 13.3359 4.60156 13.2109 4.72656C13.0859 4.85156 12.9401 4.95052 12.7734 5.02344C12.6068 5.09635 12.4297 5.13281 12.2422 5.13281C12.0547 5.13281 11.8776 5.09635 11.7109 5.02344C11.5443 4.95052 11.3984 4.85156 11.2734 4.72656C11.1484 4.60156 11.0495 4.45573 10.9766 4.28906C10.9036 4.1224 10.8672 3.94531 10.8672 3.75781C10.8672 3.57031 10.9036 3.39323 10.9766 3.22656C11.0495 3.0599 11.1484 2.91406 11.2734 2.78906C11.3984 2.66406 11.5443 2.5651 11.7109 2.49219C11.8776 2.41927 12.0547 2.38281 12.2422 2.38281Z" fill="#C5C5C5" />
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -0,0 +1,6 @@
<!--Copyright (c) Microsoft Corporation. All rights reserved.-->
<!--Copyright (C) 2020 TypeFox and others.-->
<!--Copied from https://raw.githubusercontent.com/microsoft/vscode-icons/9c90ce81b1f3c309000b80da0763aa09975a85f0/icons/light/loading.svg -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 0.75C8.17188 0.75 8.33333 0.783854 8.48438 0.851562C8.63542 0.914062 8.76823 1.0026 8.88281 1.11719C8.9974 1.23177 9.08594 1.36458 9.14844 1.51562C9.21615 1.66667 9.25 1.82812 9.25 2C9.25 2.17188 9.21615 2.33333 9.14844 2.48438C9.08594 2.63542 8.9974 2.76823 8.88281 2.88281C8.76823 2.9974 8.63542 3.08854 8.48438 3.15625C8.33333 3.21875 8.17188 3.25 8 3.25C7.82812 3.25 7.66667 3.21875 7.51562 3.15625C7.36458 3.08854 7.23177 2.9974 7.11719 2.88281C7.0026 2.76823 6.91146 2.63542 6.84375 2.48438C6.78125 2.33333 6.75 2.17188 6.75 2C6.75 1.82812 6.78125 1.66667 6.84375 1.51562C6.91146 1.36458 7.0026 1.23177 7.11719 1.11719C7.23177 1.0026 7.36458 0.914062 7.51562 0.851562C7.66667 0.783854 7.82812 0.75 8 0.75ZM2.63281 3.75781C2.63281 3.60156 2.66146 3.45573 2.71875 3.32031C2.77604 3.1849 2.85417 3.06771 2.95312 2.96875C3.05729 2.86458 3.17708 2.78385 3.3125 2.72656C3.45312 2.66406 3.60156 2.63281 3.75781 2.63281C3.91406 2.63281 4.0599 2.66406 4.19531 2.72656C4.33073 2.78385 4.44792 2.86458 4.54688 2.96875C4.65104 3.06771 4.73177 3.1849 4.78906 3.32031C4.85156 3.45573 4.88281 3.60156 4.88281 3.75781C4.88281 3.91406 4.85156 4.0625 4.78906 4.20312C4.73177 4.33854 4.65104 4.45833 4.54688 4.5625C4.44792 4.66146 4.33073 4.73958 4.19531 4.79688C4.0599 4.85417 3.91406 4.88281 3.75781 4.88281C3.60156 4.88281 3.45312 4.85417 3.3125 4.79688C3.17708 4.73958 3.05729 4.66146 2.95312 4.5625C2.85417 4.45833 2.77604 4.33854 2.71875 4.20312C2.66146 4.0625 2.63281 3.91406 2.63281 3.75781ZM2 7C2.14062 7 2.27083 7.02604 2.39062 7.07812C2.51042 7.13021 2.61458 7.20312 2.70312 7.29688C2.79688 7.38542 2.86979 7.48958 2.92188 7.60938C2.97396 7.72917 3 7.85938 3 8C3 8.14062 2.97396 8.27083 2.92188 8.39062C2.86979 8.51042 2.79688 8.61719 2.70312 8.71094C2.61458 8.79948 2.51042 8.86979 2.39062 8.92188C2.27083 8.97396 2.14062 9 2 9C1.85938 9 1.72917 8.97396 1.60938 8.92188C1.48958 8.86979 1.38281 8.79948 1.28906 8.71094C1.20052 8.61719 1.13021 8.51042 1.07812 8.39062C1.02604 8.27083 1 8.14062 1 8C1 7.85938 1.02604 7.72917 1.07812 7.60938C1.13021 7.48958 1.20052 7.38542 1.28906 7.29688C1.38281 7.20312 1.48958 7.13021 1.60938 7.07812C1.72917 7.02604 1.85938 7 2 7ZM2.88281 12.2422C2.88281 12.1224 2.90625 12.0104 2.95312 11.9062C3 11.7969 3.0625 11.7031 3.14062 11.625C3.21875 11.5469 3.3099 11.4844 3.41406 11.4375C3.52344 11.3906 3.63802 11.3672 3.75781 11.3672C3.8776 11.3672 3.98958 11.3906 4.09375 11.4375C4.20312 11.4844 4.29688 11.5469 4.375 11.625C4.45312 11.7031 4.51562 11.7969 4.5625 11.9062C4.60938 12.0104 4.63281 12.1224 4.63281 12.2422C4.63281 12.362 4.60938 12.4766 4.5625 12.5859C4.51562 12.6901 4.45312 12.7812 4.375 12.8594C4.29688 12.9375 4.20312 13 4.09375 13.0469C3.98958 13.0938 3.8776 13.1172 3.75781 13.1172C3.63802 13.1172 3.52344 13.0938 3.41406 13.0469C3.3099 13 3.21875 12.9375 3.14062 12.8594C3.0625 12.7812 3 12.6901 2.95312 12.5859C2.90625 12.4766 2.88281 12.362 2.88281 12.2422ZM8 13.25C8.20833 13.25 8.38542 13.3229 8.53125 13.4688C8.67708 13.6146 8.75 13.7917 8.75 14C8.75 14.2083 8.67708 14.3854 8.53125 14.5312C8.38542 14.6771 8.20833 14.75 8 14.75C7.79167 14.75 7.61458 14.6771 7.46875 14.5312C7.32292 14.3854 7.25 14.2083 7.25 14C7.25 13.7917 7.32292 13.6146 7.46875 13.4688C7.61458 13.3229 7.79167 13.25 8 13.25ZM11.6172 12.2422C11.6172 12.0651 11.6771 11.9167 11.7969 11.7969C11.9167 11.6771 12.0651 11.6172 12.2422 11.6172C12.4193 11.6172 12.5677 11.6771 12.6875 11.7969C12.8073 11.9167 12.8672 12.0651 12.8672 12.2422C12.8672 12.4193 12.8073 12.5677 12.6875 12.6875C12.5677 12.8073 12.4193 12.8672 12.2422 12.8672C12.0651 12.8672 11.9167 12.8073 11.7969 12.6875C11.6771 12.5677 11.6172 12.4193 11.6172 12.2422ZM14 7.5C14.1354 7.5 14.2526 7.54948 14.3516 7.64844C14.4505 7.7474 14.5 7.86458 14.5 8C14.5 8.13542 14.4505 8.2526 14.3516 8.35156C14.2526 8.45052 14.1354 8.5 14 8.5C13.8646 8.5 13.7474 8.45052 13.6484 8.35156C13.5495 8.2526 13.5 8.13542 13.5 8C13.5 7.86458 13.5495 7.7474 13.6484 7.64844C13.7474 7.54948 13.8646 7.5 14 7.5ZM12.2422 2.38281C12.4297 2.38281 12.6068 2.41927 12.7734 2.49219C12.9401 2.5651 13.0859 2.66406 13.2109 2.78906C13.3359 2.91406 13.4349 3.0599 13.5078 3.22656C13.5807 3.39323 13.6172 3.57031 13.6172 3.75781C13.6172 3.94531 13.5807 4.1224 13.5078 4.28906C13.4349 4.45573 13.3359 4.60156 13.2109 4.72656C13.0859 4.85156 12.9401 4.95052 12.7734 5.02344C12.6068 5.09635 12.4297 5.13281 12.2422 5.13281C12.0547 5.13281 11.8776 5.09635 11.7109 5.02344C11.5443 4.95052 11.3984 4.85156 11.2734 4.72656C11.1484 4.60156 11.0495 4.45573 10.9766 4.28906C10.9036 4.1224 10.8672 3.94531 10.8672 3.75781C10.8672 3.57031 10.9036 3.39323 10.9766 3.22656C11.0495 3.0599 11.1484 2.91406 11.2734 2.78906C11.3984 2.66406 11.5443 2.5651 11.7109 2.49219C11.8776 2.41927 12.0547 2.38281 12.2422 2.38281Z" fill="#424242" />
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -1,5 +1,5 @@
import { inject, injectable } from '@theia/core/shared/inversify';
import { URI as Uri } from 'vscode-uri';
import { URI as Uri } from '@theia/core/shared/vscode-uri';
import URI from '@theia/core/lib/common/uri';
import { Deferred } from '@theia/core/lib/common/promise-util';
import {

View File

@ -1,6 +1,5 @@
import * as React from '@theia/core/shared/react';
import { injectable, inject } from '@theia/core/shared/inversify';
import { OptionsType } from 'react-select/src/types';
import { Emitter } from '@theia/core/lib/common/event';
import { Disposable } from '@theia/core/lib/common/disposable';
import {
@ -128,9 +127,7 @@ export class MonitorWidget extends ReactWidget {
);
};
protected get lineEndings(): OptionsType<
SerialMonitorOutput.SelectOption<MonitorModel.EOL>
> {
protected get lineEndings(): SerialMonitorOutput.SelectOption<MonitorModel.EOL>[] {
return [
{
label: nls.localize('arduino/serial/noLineEndings', 'No Line Ending'),

View File

@ -20,6 +20,16 @@
@import './progress-bar.css';
@import './settings-step-input.css';
/* Revive of the `--theia-icon-loading`. The variable has been removed from Theia while IDE2 still uses is. */
/* The SVG icons are still part of Theia (1.31.1) */
/* https://github.com/arduino/arduino-ide/pull/1662#issuecomment-1324997134 */
body {
--theia-icon-loading: url(../icons/loading-light.svg);
}
body.theia-dark {
--theia-icon-loading: url(../icons/loading-dark.svg);
}
.theia-input.warning:focus {
outline-width: 1px;
outline-style: solid;

View File

@ -24,7 +24,7 @@ export class CommonFrontendContribution extends TheiaCommonFrontendContribution
CommonCommands.TOGGLE_MAXIMIZED,
CommonCommands.PIN_TAB,
CommonCommands.UNPIN_TAB,
CommonCommands.NEW_FILE,
CommonCommands.NEW_UNTITLED_FILE,
]) {
commandRegistry.unregisterCommand(command);
}

View File

@ -1,13 +0,0 @@
import { injectable } from '@theia/core/shared/inversify';
import { StatusBarImpl as TheiaStatusBarImpl } from '@theia/core/lib/browser';
@injectable()
export class StatusBarImpl extends TheiaStatusBarImpl {
override async removeElement(id: string): Promise<void> {
await this.ready;
if (this.entries.delete(id)) {
// Unlike Theia, IDE2 updates the status bar only if the element to remove was among the entries. Otherwise, it's a NOOP.
this.update();
}
}
}

View File

@ -1,61 +0,0 @@
import * as React from '@theia/core/shared/react';
import { injectable } from '@theia/core/shared/inversify';
import { LabelIcon } from '@theia/core/lib/browser/label-parser';
import {
TabBarToolbar as TheiaTabBarToolbar,
TabBarToolbarItem,
} from '@theia/core/lib/browser/shell/tab-bar-toolbar';
@injectable()
export class TabBarToolbar extends TheiaTabBarToolbar {
/**
* Copied over from Theia. Added an ID to the parent of the toolbar item (`--container`).
* CSS3 does not support parent selectors but we want to style the parent of the toolbar item.
*/
protected override renderItem(item: TabBarToolbarItem): React.ReactNode {
let innerText = '';
const classNames = [];
if (item.text) {
for (const labelPart of this.labelParser.parse(item.text)) {
if (typeof labelPart !== 'string' && LabelIcon.is(labelPart)) {
const className = `fa fa-${labelPart.name}${
labelPart.animation ? ' fa-' + labelPart.animation : ''
}`;
classNames.push(...className.split(' '));
} else {
innerText = labelPart;
}
}
}
const command = this.commands.getCommand(item.command);
const iconClass =
(typeof item.icon === 'function' && item.icon()) ||
item.icon ||
(command && command.iconClass);
if (iconClass) {
classNames.push(iconClass);
}
const tooltip = item.tooltip || (command && command.label);
return (
<div
id={`${item.id}--container`}
key={item.id}
className={`${TabBarToolbar.Styles.TAB_BAR_TOOLBAR_ITEM}${
command && this.commandIsEnabled(command.id) ? ' enabled' : ''
}`}
onMouseDown={this.onMouseDownEvent}
onMouseUp={this.onMouseUpEvent}
onMouseOut={this.onMouseUpEvent}
>
<div
id={item.id}
className={classNames.join(' ')}
onClick={this.executeCommand}
title={tooltip}
>
{innerText}
</div>
</div>
);
}
}

View File

@ -0,0 +1,26 @@
import { ThemeService as TheiaThemeService } from '@theia/core/lib/browser/theming';
import type { Theme } from '@theia/core/lib/common/theme';
import { injectable } from '@theia/core/shared/inversify';
export namespace ArduinoThemes {
export const Light: Theme = {
id: 'arduino-theme',
type: 'light',
label: 'Light (Arduino)',
editorTheme: 'arduino-theme',
};
export const Dark: Theme = {
id: 'arduino-theme-dark',
type: 'dark',
label: 'Dark (Arduino)',
editorTheme: 'arduino-theme-dark',
};
}
@injectable()
export class ThemeService extends TheiaThemeService {
protected override init(): void {
this.register(ArduinoThemes.Light, ArduinoThemes.Dark);
super.init();
}
}

View File

@ -1,4 +1,3 @@
import type { MaybePromise } from '@theia/core';
import type { Widget } from '@theia/core/lib/browser';
import { WidgetManager as TheiaWidgetManager } from '@theia/core/lib/browser/widget-manager';
import {
@ -8,7 +7,6 @@ import {
} from '@theia/core/shared/inversify';
import { EditorWidget } from '@theia/editor/lib/browser';
import { OutputWidget } from '@theia/output/lib/browser/output-widget';
import deepEqual = require('deep-equal');
import {
CurrentSketch,
SketchesServiceClientImpl,
@ -72,44 +70,4 @@ export class WidgetManager extends TheiaWidgetManager {
title.className += title.className + ` ${uncloseableClass}`;
}
}
/**
* Customized to find any existing widget based on `options` deepEquals instead of string equals.
* See https://github.com/eclipse-theia/theia/issues/11309.
*/
protected override doGetWidget<T extends Widget>(
key: string
): MaybePromise<T> | undefined {
const pendingWidget = this.findExistingWidget<T>(key);
if (pendingWidget) {
return pendingWidget as MaybePromise<T>;
}
return undefined;
}
private findExistingWidget<T extends Widget>(
key: string
): MaybePromise<T> | undefined {
const parsed = this.parseJson(key);
for (const [candidateKey, widget] of [
...this.widgetPromises.entries(),
...this.pendingWidgetPromises.entries(),
]) {
const candidate = this.parseJson(candidateKey);
if (deepEqual(candidate, parsed)) {
return widget as MaybePromise<T>;
}
}
return undefined;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private parseJson(json: string): any {
try {
return JSON.parse(json);
} catch (err) {
console.log(`Failed to parse JSON: <${json}>.`, err);
throw err;
}
}
}

View File

@ -0,0 +1,87 @@
import * as remote from '@theia/core/electron-shared/@electron/remote';
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
import { NavigatableWidget } from '@theia/core/lib/browser/navigatable-types';
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
import { Widget } from '@theia/core/lib/browser/widgets/widget';
import { WindowTitleUpdater as TheiaWindowTitleUpdater } from '@theia/core/lib/browser/window/window-title-updater';
import { ApplicationServer } from '@theia/core/lib/common/application-protocol';
import { isOSX } from '@theia/core/lib/common/os';
import {
inject,
injectable,
postConstruct,
} from '@theia/core/shared/inversify';
import { EditorWidget } from '@theia/editor/lib/browser/editor-widget';
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
@injectable()
export class WindowTitleUpdater extends TheiaWindowTitleUpdater {
@inject(ApplicationServer)
private readonly applicationServer: ApplicationServer;
@inject(ApplicationShell)
private readonly applicationShell: ApplicationShell;
@inject(WorkspaceService)
private readonly workspaceService: WorkspaceService;
private _previousRepresentedFilename: string | undefined;
private readonly applicationName =
FrontendApplicationConfigProvider.get().applicationName;
private applicationVersion: string | undefined;
@postConstruct()
protected init(): void {
setTimeout(
() =>
this.applicationServer.getApplicationInfo().then((info) => {
this.applicationVersion = info?.version;
if (this.applicationVersion) {
this.handleWidgetChange(this.applicationShell.currentWidget);
}
}),
0
);
}
protected override handleWidgetChange(widget?: Widget | undefined): void {
if (isOSX) {
this.maybeUpdateRepresentedFilename(widget);
}
// Unlike Theia, IDE2 does not want to show in the window title if the current widget is dirty or not.
// Hence, IDE2 does not track widgets but updates the window title on current widget change.
this.updateTitleWidget(widget);
}
protected override updateTitleWidget(widget?: Widget | undefined): void {
let activeEditorShort = '';
const rootName = this.workspaceService.workspace?.name ?? '';
let appName = `${this.applicationName}${
this.applicationVersion ? ` ${this.applicationVersion}` : ''
}`;
if (rootName) {
appName = ` | ${appName}`;
}
const uri = NavigatableWidget.getUri(widget);
if (uri) {
const base = uri.path.base;
// Do not show the basename of the main sketch file. Only other sketch file names are visible in the title.
if (`${rootName}.ino` !== base) {
activeEditorShort = ` - ${base} `;
}
}
this.windowTitleService.update({ rootName, appName, activeEditorShort });
}
private maybeUpdateRepresentedFilename(widget?: Widget | undefined): void {
if (widget instanceof EditorWidget) {
const { uri } = widget.editor;
const filename = uri.path.toString();
// Do not necessarily require the current window if not needed. It's a synchronous, blocking call.
if (this._previousRepresentedFilename !== filename) {
const currentWindow = remote.getCurrentWindow();
currentWindow.setRepresentedFilename(uri.path.toString());
this._previousRepresentedFilename = filename;
}
}
}
}

View File

@ -1,5 +1,9 @@
import debounce = require('p-debounce');
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
import {
inject,
injectable,
postConstruct,
} from '@theia/core/shared/inversify';
import URI from '@theia/core/lib/common/uri';
import { Event, Emitter } from '@theia/core/lib/common/event';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
@ -126,7 +130,7 @@ export class DebugConfigurationManager extends TheiaDebugConfigurationManager {
const uri = tempFolderUri.resolve('launch.json');
const { value } = await this.fileService.read(uri);
const configurations = DebugConfigurationModel.parse(JSON.parse(value));
return { uri, configurations };
return { uri, configurations, compounds: [] };
} catch (err) {
if (
err instanceof FileOperationError &&

View File

@ -29,6 +29,7 @@ export class DebugConfigurationModel extends TheiaDebugConfigurationModel {
return {
uri: this.configUri,
configurations: this.config,
compounds: [],
};
}
}

View File

@ -1,90 +0,0 @@
import { injectable } from '@theia/core/shared/inversify';
import { DebugError } from '@theia/debug/lib/common/debug-service';
import { DebugSession } from '@theia/debug/lib/browser/debug-session';
import { DebugSessionOptions } from '@theia/debug/lib/browser/debug-session-options';
import { DebugSessionManager as TheiaDebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager';
import { nls } from '@theia/core/lib/common';
@injectable()
export class DebugSessionManager extends TheiaDebugSessionManager {
override async start(options: DebugSessionOptions): Promise<DebugSession | undefined> {
return this.progressService.withProgress(
nls.localize('theia/debug/start', 'Start...'),
'debug',
async () => {
try {
// Only save when dirty. To avoid saving temporary sketches.
// This is a quick fix for not saving the editor when there are no dirty editors.
// // https://github.com/bcmi-labs/arduino-editor/pull/172#issuecomment-741831888
if (this.shell.canSaveAll()) {
await this.shell.saveAll();
}
await this.fireWillStartDebugSession();
const resolved = await this.resolveConfiguration(options);
//#region "cherry-picked" from here: https://github.com/eclipse-theia/theia/commit/e6b57ba4edabf797f3b4e67bc2968cdb8cc25b1e#diff-08e04edb57cd2af199382337aaf1dbdb31171b37ae4ab38a38d36cd77bc656c7R196-R207
if (!resolved) {
// As per vscode API: https://code.visualstudio.com/api/references/vscode-api#DebugConfigurationProvider
// "Returning the value 'undefined' prevents the debug session from starting.
// Returning the value 'null' prevents the debug session from starting and opens the
// underlying debug configuration instead."
if (resolved === null) {
this.debugConfigurationManager.openConfiguration();
}
return undefined;
}
//#endregion end of cherry-pick
// preLaunchTask isn't run in case of auto restart as well as postDebugTask
if (!options.configuration.__restart) {
const taskRun = await this.runTask(
options.workspaceFolderUri,
resolved.configuration.preLaunchTask,
true
);
if (!taskRun) {
return undefined;
}
}
const sessionId = await this.debug.createDebugSession(
resolved.configuration
);
return this.doStart(sessionId, resolved);
} catch (e) {
if (DebugError.NotFound.is(e)) {
this.messageService.error(
nls.localize(
'theia/debug/typeNotSupported',
'The debug session type "{0}" is not supported.',
e.data.type
)
);
return undefined;
}
this.messageService.error(
nls.localize(
'theia/debug/startError',
'There was an error starting the debug session, check the logs for more details.'
)
);
console.error('Error starting the debug session', e);
throw e;
}
}
);
}
override async terminateSession(session?: DebugSession): Promise<void> {
if (!session) {
this.updateCurrentSession(this._currentSession);
session = this._currentSession;
}
// The cortex-debug extension does not respond to close requests
// So we simply terminate the debug session immediately
// Alternatively the `super.terminateSession` call will terminate it after 5 seconds without a response
await this.debug.terminateDebugSession(session!.id);
await super.terminateSession(session);
}
}

View File

@ -1,17 +0,0 @@
import { injectable, inject } from '@theia/core/shared/inversify';
import {
AbstractDialog as TheiaAbstractDialog,
codiconArray,
DialogProps,
} from '@theia/core/lib/browser';
@injectable()
export abstract class AbstractDialog<T> extends TheiaAbstractDialog<T> {
constructor(@inject(DialogProps) protected override readonly props: DialogProps) {
super(props);
this.closeCrossNode.classList.remove(...codiconArray('close'));
this.closeCrossNode.classList.add('fa', 'fa-close');
}
}

View File

@ -0,0 +1,63 @@
import {
AbstractDialog as TheiaAbstractDialog,
DialogProps,
} from '@theia/core/lib/browser/dialogs';
import { ReactDialog as TheiaReactDialog } from '@theia/core/lib/browser/dialogs/react-dialog';
import { codiconArray, Message } from '@theia/core/lib/browser/widgets/widget';
import {
Disposable,
DisposableCollection,
} from '@theia/core/lib/common/disposable';
import { inject, injectable } from '@theia/core/shared/inversify';
import * as React from '@theia/core/shared/react';
import { createRoot } from '@theia/core/shared/react-dom/client';
@injectable()
export abstract class AbstractDialog<T> extends TheiaAbstractDialog<T> {
constructor(
@inject(DialogProps) protected override readonly props: DialogProps
) {
super(props);
this.closeCrossNode.classList.remove(...codiconArray('close'));
this.closeCrossNode.classList.add('fa', 'fa-close');
}
}
@injectable()
export abstract class ReactDialog<T> extends TheiaReactDialog<T> {
protected override onUpdateRequest(msg: Message): void {
// This is tricky to bypass the default Theia code.
// Otherwise, there is a warning when opening the dialog for the second time.
// You are calling ReactDOMClient.createRoot() on a container that has already been passed to createRoot() before. Instead, call root.render() on the existing root instead if you want to update it.
const disposables = new DisposableCollection();
if (!this.isMounted) {
// toggle the `isMounted` logic for the time being of the super call so that the `createRoot` does not run
this.isMounted = true;
disposables.push(Disposable.create(() => (this.isMounted = false)));
}
// Always unset the `contentNodeRoot` so there is no double update when calling super.
const restoreContentNodeRoot = this.contentNodeRoot;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.contentNodeRoot as any) = undefined;
disposables.push(
Disposable.create(() => (this.contentNodeRoot = restoreContentNodeRoot))
);
try {
super.onUpdateRequest(msg);
} finally {
disposables.dispose();
}
// Use the patched rendering.
if (!this.isMounted) {
this.contentNodeRoot = createRoot(this.contentNode);
// Resetting the prop is missing from the Theia code.
// https://github.com/eclipse-theia/theia/blob/v1.31.1/packages/core/src/browser/dialogs/react-dialog.tsx#L41-L47
this.isMounted = true;
}
this.contentNodeRoot?.render(<>{this.render()}</>);
}
}

View File

@ -20,7 +20,7 @@ export class EditorWidgetFactory extends TheiaEditorWidgetFactory {
protected override async createEditor(
uri: URI,
options: NavigatableWidgetOptions
options?: NavigatableWidgetOptions
): Promise<EditorWidget> {
const widget = await super.createEditor(uri, options);
return this.maybeUpdateCaption(widget);

View File

@ -3,7 +3,7 @@ import {
injectable,
postConstruct,
} from '@theia/core/shared/inversify';
import { Diagnostic } from 'vscode-languageserver-types';
import { Diagnostic } from '@theia/core/shared/vscode-languageserver-types';
import URI from '@theia/core/lib/common/uri';
import { ILogger } from '@theia/core';
import { Marker } from '@theia/markers/lib/common/marker';

View File

@ -1,5 +1,4 @@
import * as React from '@theia/core/shared/react';
import * as ReactDOM from '@theia/core/shared/react-dom';
import {
inject,
injectable,
@ -25,15 +24,14 @@ export class NotificationsRenderer extends TheiaNotificationsRenderer {
}
protected override render(): void {
ReactDOM.render(
this.containerRoot.render(
<div>
<NotificationToastsComponent
manager={this.manager}
corePreferences={this.corePreferences}
/>
<NotificationCenterComponent manager={this.manager} />
</div>,
this.container
</div>
);
}
}

View File

@ -0,0 +1,23 @@
import { injectable } from '@theia/core/shared/inversify';
import { MonacoThemingService as TheiaMonacoThemingService } from '@theia/monaco/lib/browser/monaco-theming-service';
import { ArduinoThemes } from '../core/theming';
@injectable()
export class MonacoThemingService extends TheiaMonacoThemingService {
override initialize(): void {
super.initialize();
const { Light, Dark } = ArduinoThemes;
this.registerParsedTheme({
id: Light.id,
label: Light.label,
uiTheme: 'vs',
json: require('../../../../src/browser/data/default.color-theme.json'),
});
this.registerParsedTheme({
id: Dark.id,
label: Dark.label,
uiTheme: 'vs-dark',
json: require('../../../../src/browser/data/dark.color-theme.json'),
});
}
}

View File

@ -0,0 +1,32 @@
import { KeybindingRegistry } from '@theia/core/lib/browser/keybinding';
import { CommandRegistry } from '@theia/core/lib/common/command';
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
import { injectable } from '@theia/core/shared/inversify';
import {
TypeHierarchyCommands,
TypeHierarchyContribution as TheiaTypeHierarchyContribution,
} from '@theia/typehierarchy/lib/browser/typehierarchy-contribution';
@injectable()
export class TypeHierarchyContribution extends TheiaTypeHierarchyContribution {
protected override init(): void {
// NOOP
}
override registerCommands(registry: CommandRegistry): void {
super.registerCommands(registry);
registry.unregisterCommand(TypeHierarchyCommands.OPEN_SUBTYPE.id);
registry.unregisterCommand(TypeHierarchyCommands.OPEN_SUPERTYPE.id);
}
override registerMenus(registry: MenuModelRegistry): void {
super.registerMenus(registry);
registry.unregisterMenuAction(TypeHierarchyCommands.OPEN_SUBTYPE.id);
registry.unregisterMenuAction(TypeHierarchyCommands.OPEN_SUPERTYPE.id);
}
override registerKeybindings(registry: KeybindingRegistry): void {
super.registerKeybindings(registry);
registry.unregisterKeybinding(TypeHierarchyCommands.OPEN_SUBTYPE.id);
}
}

View File

@ -0,0 +1,9 @@
import { injectable } from '@theia/core/shared/inversify';
import { TypeHierarchyServiceProvider as TheiaTypeHierarchyServiceProvider } from '@theia/typehierarchy/lib/browser/typehierarchy-service';
@injectable()
export class TypeHierarchyServiceProvider extends TheiaTypeHierarchyServiceProvider {
override init(): void {
// NOOP
}
}

View File

@ -1,58 +1,37 @@
import * as remote from '@theia/core/electron-shared/@electron/remote';
import { injectable, inject, named } from '@theia/core/shared/inversify';
import { ContributionProvider } from '@theia/core/lib/common/contribution-provider';
import URI from '@theia/core/lib/common/uri';
import { EditorWidget } from '@theia/editor/lib/browser';
import { ApplicationServer } from '@theia/core/lib/common/application-protocol';
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
import { FocusTracker, Widget } from '@theia/core/lib/browser';
import {
DEFAULT_WINDOW_HASH,
NewWindowOptions,
} from '@theia/core/lib/common/window';
import { inject, injectable, named } from '@theia/core/shared/inversify';
import { FileStat } from '@theia/filesystem/lib/common/files';
import {
WorkspaceInput,
WorkspaceService as TheiaWorkspaceService,
} from '@theia/workspace/lib/browser/workspace-service';
import {
SketchesService,
Sketch,
SketchesError,
SketchesService,
} from '../../../common/protocol/sketches-service';
import { FileStat } from '@theia/filesystem/lib/common/files';
import {
StartupTask,
StartupTaskProvider,
} from '../../../electron-common/startup-task';
import { WindowServiceExt } from '../core/window-service-ext';
import { ContributionProvider } from '@theia/core/lib/common/contribution-provider';
@injectable()
export class WorkspaceService extends TheiaWorkspaceService {
@inject(SketchesService)
private readonly sketchService: SketchesService;
@inject(ApplicationServer)
private readonly applicationServer: ApplicationServer;
@inject(WindowServiceExt)
private readonly windowServiceExt: WindowServiceExt;
@inject(ContributionProvider)
@named(StartupTaskProvider)
private readonly providers: ContributionProvider<StartupTaskProvider>;
private version?: string;
private _workspaceError: Error | undefined;
async onStart(application: FrontendApplication): Promise<void> {
const info = await this.applicationServer.getApplicationInfo();
this.version = info?.version;
application.shell.onDidChangeCurrentWidget(
this.onCurrentWidgetChange.bind(this)
);
const newValue = application.shell.currentWidget
? application.shell.currentWidget
: null;
this.onCurrentWidgetChange({ newValue, oldValue: null });
}
get workspaceError(): Error | undefined {
return this._workspaceError;
}
@ -121,58 +100,6 @@ export class WorkspaceService extends TheiaWorkspaceService {
}
}
/**
* Copied from Theia as-is to be able to pass the original `options` down.
*/
protected override async doOpen(
uri: URI,
options?: WorkspaceInput
): Promise<URI | undefined> {
const stat = await this.toFileStat(uri);
if (stat) {
if (!stat.isDirectory && !this.isWorkspaceFile(stat)) {
const message = `Not a valid workspace: ${uri.path.toString()}`;
this.messageService.error(message);
throw new Error(message);
}
// The same window has to be preserved too (instead of opening a new one), if the workspace root is not yet available and we are setting it for the first time.
// Option passed as parameter has the highest priority (for api developers), then the preference, then the default.
await this.roots;
const { preserveWindow } = {
preserveWindow:
this.preferences['workspace.preserveWindow'] || !this.opened,
...options,
};
await this.server.setMostRecentlyUsedWorkspace(uri.toString());
if (preserveWindow) {
this._workspace = stat;
}
this.openWindow(stat, Object.assign(options ?? {}, { preserveWindow })); // Unlike Theia, IDE2 passes the whole `input` downstream and not only { preserveWindow }
return;
}
throw new Error(
'Invalid workspace root URI. Expected an existing directory or workspace file.'
);
}
/**
* Copied from Theia. Can pass the `options` further down the chain.
*/
protected override openWindow(uri: FileStat, options?: WorkspaceInput): void {
const workspacePath = uri.resource.path.toString();
if (this.shouldPreserveWindow(options)) {
this.reloadWindow(options); // Unlike Theia, IDE2 passes the `input` downstream.
} else {
try {
this.openNewWindow(workspacePath, options); // Unlike Theia, IDE2 passes the `input` downstream.
} catch (error) {
// Fall back to reloading the current window in case the browser has blocked the new window
this._workspace = uri;
this.logger.error(error.toString()).then(() => this.reloadWindow());
}
}
}
protected override reloadWindow(options?: WorkspaceInput): void {
const tasks = this.tasks(options);
this.setURLFragment(this._workspace?.resource.path.toString() || '');
@ -192,6 +119,10 @@ export class WorkspaceService extends TheiaWorkspaceService {
);
}
protected override updateTitle(): void {
// NOOP. IDE2 handles the `window.title` updates solely via the customized `WindowTitleUpdater`.
}
private tasks(options?: WorkspaceInput): StartupTask[] {
const tasks = this.providers
.getContributions()
@ -202,37 +133,4 @@ export class WorkspaceService extends TheiaWorkspaceService {
}
return tasks;
}
protected onCurrentWidgetChange({
newValue,
}: FocusTracker.IChangedArgs<Widget>): void {
if (newValue instanceof EditorWidget) {
const { uri } = newValue.editor;
const currentWindow = remote.getCurrentWindow();
currentWindow.setRepresentedFilename(uri.path.toString());
if (Sketch.isSketchFile(uri.toString())) {
this.updateTitle();
} else {
const title = this.workspaceTitle;
const fileName = this.labelProvider.getName(uri);
document.title = this.formatTitle(
title ? `${title} - ${fileName}` : fileName
);
}
} else {
this.updateTitle();
}
}
protected override formatTitle(title?: string): string {
const version = this.version ? ` ${this.version}` : '';
const name = `${this.applicationName} ${version}`;
return title ? `${title} | ${name}` : name;
}
protected get workspaceTitle(): string | undefined {
if (this.workspace) {
return this.labelProvider.getName(this.workspace.resource);
}
}
}

View File

@ -1,17 +1,22 @@
import * as React from '@theia/core/shared/react';
import Select from 'react-select';
import { Styles } from 'react-select/src/styles';
import { Props } from 'react-select/src/components';
import { ThemeConfig } from 'react-select/src/theme';
import type { StylesConfig } from 'react-select/dist/declarations/src/styles';
import type { ThemeConfig } from 'react-select/dist/declarations/src/theme';
import type { GroupBase } from 'react-select/dist/declarations/src/types';
import type { StateManagerProps } from 'react-select/dist/declarations/src/useStateManager';
export class ArduinoSelect<T> extends Select<T> {
constructor(props: Readonly<Props<T, false>>) {
export class ArduinoSelect<
Option,
IsMulti extends boolean = false,
Group extends GroupBase<Option> = GroupBase<Option>
> extends React.Component<StateManagerProps<Option, IsMulti, Group>> {
constructor(props: StateManagerProps<Option, IsMulti, Group>) {
super(props);
}
override render(): React.ReactNode {
const controlHeight = 27; // from `monitor.css` -> `.serial-monitor-container .head` (`height: 27px;`)
const styles: Styles<T, false> = {
const styles: StylesConfig = {
control: (styles) => ({
...styles,
minWidth: 120,

View File

@ -1,5 +1,5 @@
import * as React from '@theia/core/shared/react';
import * as ReactDOM from '@theia/core/shared/react-dom';
import type { Root } from '@theia/core/shared/react-dom/client';
import {
inject,
injectable,
@ -49,8 +49,8 @@ export class CloudSketchbookCompositeWidget extends BaseSketchbookCompositeWidge
return this.cloudSketchbookTreeWidget;
}
protected renderFooter(footerNode: HTMLElement): void {
ReactDOM.render(
protected renderFooter(footerRoot: Root): void {
footerRoot.render(
<>
{this._session && (
<CreateNew
@ -67,8 +67,7 @@ export class CloudSketchbookCompositeWidget extends BaseSketchbookCompositeWidge
}
authenticationService={this.authenticationService}
/>
</>,
footerNode
</>
);
}

View File

@ -1,5 +1,5 @@
import * as React from '@theia/core/shared/react';
import * as ReactDOM from '@theia/core/shared/react-dom';
import { createRoot, Root } from '@theia/core/shared/react-dom/client';
import { inject, injectable } from '@theia/core/shared/inversify';
import { nls } from '@theia/core/lib/common/nls';
import { Widget } from '@theia/core/shared/@phosphor/widgets';
@ -18,29 +18,30 @@ export abstract class BaseSketchbookCompositeWidget<
protected readonly commandService: CommandService;
private readonly compositeNode: HTMLElement;
private readonly footerNode: HTMLElement;
private readonly footerRoot: Root;
constructor() {
super();
this.compositeNode = document.createElement('div');
this.compositeNode.classList.add('composite-node');
this.footerNode = document.createElement('div');
this.footerNode.classList.add('footer-node');
this.compositeNode.appendChild(this.footerNode);
const footerNode = document.createElement('div');
footerNode.classList.add('footer-node');
this.compositeNode.appendChild(footerNode);
this.footerRoot = createRoot(footerNode);
this.node.appendChild(this.compositeNode);
this.title.closable = false;
}
abstract get treeWidget(): TW;
protected abstract renderFooter(footerNode: HTMLElement): void;
protected abstract renderFooter(footerRoot: Root): void;
protected updateFooter(): void {
this.renderFooter(this.footerNode);
this.renderFooter(this.footerRoot);
}
protected override onAfterAttach(message: Message): void {
super.onAfterAttach(message);
Widget.attach(this.treeWidget, this.compositeNode);
this.renderFooter(this.footerNode);
this.renderFooter(this.footerRoot);
this.toDisposeOnDetach.push(
Disposable.create(() => Widget.detach(this.treeWidget))
);
@ -77,13 +78,12 @@ export class SketchbookCompositeWidget extends BaseSketchbookCompositeWidget<Ske
return this.sketchbookTreeWidget;
}
protected renderFooter(footerNode: HTMLElement): void {
ReactDOM.render(
protected renderFooter(footerRoot: Root): void {
footerRoot.render(
<CreateNew
label={nls.localize('arduino/sketchbook/newSketch', 'New Sketch')}
onClick={this.onDidClickCreateNew}
/>,
footerNode
/>
);
}

View File

@ -1,23 +1,25 @@
import { inject, injectable } from '@theia/core/shared/inversify';
import * as remote from '@theia/core/electron-shared/@electron/remote';
import { isOSX } from '@theia/core/lib/common/os';
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import {
ActionMenuNode,
CompositeMenuNode,
CommandMenuNode,
CompoundMenuNode,
CompoundMenuNodeRole,
MAIN_MENU_BAR,
MenuNode,
MenuPath,
} from '@theia/core/lib/common/menu';
import { isOSX } from '@theia/core/lib/common/os';
import {
ElectronMainMenuFactory as TheiaElectronMainMenuFactory,
ElectronMenuItemRole,
ElectronMenuOptions,
} from '@theia/core/lib/electron-browser/menu/electron-main-menu-factory';
import { inject, injectable } from '@theia/core/shared/inversify';
import {
ArduinoMenus,
PlaceholderMenuNode,
} from '../../../browser/menu/arduino-menus';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
@injectable()
export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
@ -40,7 +42,9 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
override createElectronMenuBar(): Electron.Menu {
this._toggledCommands.clear(); // https://github.com/eclipse-theia/theia/issues/8977
const menuModel = this.menuProvider.getMenu(MAIN_MENU_BAR);
const template = this.fillMenuTemplate([], menuModel);
const template = this.fillMenuTemplate([], menuModel, [], {
rootMenuPath: MAIN_MENU_BAR,
});
if (isOSX) {
template.unshift(this.createOSXMenu());
}
@ -68,11 +72,15 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
override createElectronContextMenu(
menuPath: MenuPath,
args?: any[]
// eslint-disable-next-line @typescript-eslint/no-explicit-any
args?: any[],
context?: HTMLElement
): Electron.Menu {
const menuModel = this.menuProvider.getMenu(menuPath);
const template = this.fillMenuTemplate([], menuModel, args, {
showDisabled: false,
context,
rootMenuPath: menuPath,
});
return remote.Menu.buildFromTemplate(this.escapeAmpersand(template));
}
@ -96,20 +104,26 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
protected override createOSXMenu(): Electron.MenuItemConstructorOptions {
const { submenu } = super.createOSXMenu();
const label = 'Arduino IDE';
const label = FrontendApplicationConfigProvider.get().applicationName;
if (!!submenu && Array.isArray(submenu)) {
const [, , /* about */ /* preferences */ ...rest] = submenu;
const about = this.fillMenuTemplate(
[],
this.menuProvider.getMenu(ArduinoMenus.HELP__ABOUT_GROUP)
this.menuProvider.getMenu(ArduinoMenus.HELP__ABOUT_GROUP),
[],
{ rootMenuPath: ArduinoMenus.HELP__ABOUT_GROUP }
);
const preferences = this.fillMenuTemplate(
[],
this.menuProvider.getMenu(ArduinoMenus.FILE__PREFERENCES_GROUP)
this.menuProvider.getMenu(ArduinoMenus.FILE__PREFERENCES_GROUP),
[],
{ rootMenuPath: ArduinoMenus.FILE__PREFERENCES_GROUP }
);
const advanced = this.fillMenuTemplate(
[],
this.menuProvider.getMenu(ArduinoMenus.FILE__ADVANCED_GROUP)
this.menuProvider.getMenu(ArduinoMenus.FILE__ADVANCED_GROUP),
[],
{ rootMenuPath: ArduinoMenus.FILE__ADVANCED_GROUP }
);
return {
label,
@ -126,7 +140,7 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
return { label, submenu };
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-vars
protected override roleFor(id: string): ElectronMenuItemRole | undefined {
// MenuItem `roles` are completely broken on macOS:
// - https://github.com/eclipse-theia/theia/issues/11217,
@ -135,135 +149,133 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
return undefined;
}
protected override handleElectronDefault(
menuNode: MenuNode,
args: any[] = [],
options?: ElectronMenuOptions
protected override fillMenuTemplate(
parentItems: Electron.MenuItemConstructorOptions[],
menuModel: MenuNode,
args: unknown[] | undefined,
options: ElectronMenuOptions
): Electron.MenuItemConstructorOptions[] {
if (menuNode instanceof PlaceholderMenuNode) {
return [
{
label: menuNode.label,
enabled: false,
visible: true,
},
];
if (menuModel instanceof PlaceholderMenuNode) {
parentItems.push({
label: menuModel.label,
enabled: false,
visible: true,
});
} else {
this.superFillMenuTemplate(parentItems, menuModel, args, options);
}
return [];
return parentItems;
}
// Copied from 1.25.0 Theia as is to customize the enablement of the menu items.
// Source: https://github.com/eclipse-theia/theia/blob/ca417a31e402bd35717d3314bf6254049d1dae44/packages/core/src/electron-browser/menu/electron-main-menu-factory.ts#L125-L220
// Copied from 1.31.1 Theia as is to customize the enablement of the menu items.
// Source: https://github.com/eclipse-theia/theia/blob/5e641750af83383f2ce0cb3432ec333df70778a8/packages/core/src/electron-browser/menu/electron-main-menu-factory.ts#L132-L203
// See https://github.com/arduino/arduino-ide/issues/1533
protected override fillMenuTemplate(
items: Electron.MenuItemConstructorOptions[],
menuModel: CompositeMenuNode,
args: any[] = [],
options?: ElectronMenuOptions
private superFillMenuTemplate(
parentItems: Electron.MenuItemConstructorOptions[],
menu: MenuNode,
args: unknown[] = [],
options: ElectronMenuOptions
): Electron.MenuItemConstructorOptions[] {
const showDisabled =
options?.showDisabled === undefined ? true : options?.showDisabled;
for (const menu of menuModel.children) {
if (menu instanceof CompositeMenuNode) {
if (menu.children.length > 0) {
// do not render empty nodes
if (menu.isSubmenu) {
// submenu node
const submenu = this.fillMenuTemplate([], menu, args, options);
if (submenu.length === 0) {
continue;
}
items.push({
label: menu.label,
submenu,
});
} else {
// group node
// process children
const submenu = this.fillMenuTemplate([], menu, args, options);
if (submenu.length === 0) {
continue;
}
if (items.length > 0) {
// do not put a separator above the first group
items.push({
type: 'separator',
});
}
// render children
items.push(...submenu);
}
}
} else if (menu instanceof ActionMenuNode) {
const node =
menu.altNode && this.context.altPressed ? menu.altNode : menu;
const commandId = node.action.commandId;
// That is only a sanity check at application startup.
if (!this.commandRegistry.getCommand(commandId)) {
console.debug(
`Skipping menu item with missing command: "${commandId}".`
);
continue;
}
const showDisabled = options?.showDisabled !== false;
if (
CompoundMenuNode.is(menu) &&
menu.children.length &&
this.undefinedOrMatch(menu.when, options.context)
) {
const role = CompoundMenuNode.getRole(menu);
if (role === CompoundMenuNodeRole.Group && menu.id === 'inline') {
return parentItems;
}
const children = CompoundMenuNode.getFlatChildren(menu.children);
const myItems: Electron.MenuItemConstructorOptions[] = [];
children.forEach((child) =>
this.fillMenuTemplate(myItems, child, args, options)
);
if (myItems.length === 0) {
return parentItems;
}
if (role === CompoundMenuNodeRole.Submenu) {
parentItems.push({ label: menu.label, submenu: myItems });
} else if (role === CompoundMenuNodeRole.Group && menu.id !== 'inline') {
if (
!this.commandRegistry.isVisible(commandId, ...args) ||
(!!node.action.when &&
!this.contextKeyService.match(node.action.when))
parentItems.length &&
parentItems[parentItems.length - 1].type !== 'separator'
) {
continue;
parentItems.push({ type: 'separator' });
}
parentItems.push(...myItems);
parentItems.push({ type: 'separator' });
}
} else if (menu.command) {
const node =
menu.altNode && this.context.altPressed
? menu.altNode
: (menu as MenuNode & CommandMenuNode);
const commandId = node.command;
// We should omit rendering context-menu items which are disabled.
if (
!showDisabled &&
!this.commandRegistry.isEnabled(commandId, ...args)
) {
continue;
// That is only a sanity check at application startup.
if (!this.commandRegistry.getCommand(commandId)) {
console.debug(
`Skipping menu item with missing command: "${commandId}".`
);
return parentItems;
}
if (
!this.menuCommandExecutor.isVisible(
options.rootMenuPath,
commandId,
...args
) ||
!this.undefinedOrMatch(node.when, options.context)
) {
return parentItems;
}
// We should omit rendering context-menu items which are disabled.
if (
!showDisabled &&
!this.menuCommandExecutor.isEnabled(
options.rootMenuPath,
commandId,
...args
)
) {
return parentItems;
}
const bindings =
this.keybindingRegistry.getKeybindingsForCommand(commandId);
const accelerator = bindings[0] && this.acceleratorFor(bindings[0]);
const menuItem: Electron.MenuItemConstructorOptions = {
id: node.id,
label: node.label,
type: this.commandRegistry.getToggledHandler(commandId, ...args)
? 'checkbox'
: 'normal',
checked: this.commandRegistry.isToggled(commandId, ...args),
enabled: this.commandRegistry.isEnabled(commandId, ...args), // Unlike Theia https://github.com/eclipse-theia/theia/blob/v1.31.1/packages/core/src/electron-browser/menu/electron-main-menu-factory.ts#L183
visible: true,
accelerator,
click: () => this.execute(commandId, args, options.rootMenuPath),
};
if (isOSX) {
const role = this.roleFor(node.id);
if (role) {
menuItem.role = role;
delete menuItem.click;
}
}
parentItems.push(menuItem);
const bindings =
this.keybindingRegistry.getKeybindingsForCommand(commandId);
const accelerator = bindings[0] && this.acceleratorFor(bindings[0]);
const menuItem: Electron.MenuItemConstructorOptions = {
id: node.id,
label: node.label,
type: this.commandRegistry.getToggledHandler(commandId, ...args)
? 'checkbox'
: 'normal',
checked: this.commandRegistry.isToggled(commandId, ...args),
enabled: this.commandRegistry.isEnabled(commandId, ...args), // Unlike Theia https://github.com/eclipse-theia/theia/blob/ca417a31e402bd35717d3314bf6254049d1dae44/packages/core/src/electron-browser/menu/electron-main-menu-factory.ts#L197
visible: true,
accelerator,
click: () => this.execute(commandId, args),
};
if (isOSX) {
const role = this.roleFor(node.id);
if (role) {
menuItem.role = role;
delete menuItem.click;
}
}
items.push(menuItem);
if (this.commandRegistry.getToggledHandler(commandId, ...args)) {
this._toggledCommands.add(commandId);
}
} else {
items.push(...this.handleElectronDefault(menu, args, options));
if (this.commandRegistry.getToggledHandler(commandId, ...args)) {
this._toggledCommands.add(commandId);
}
}
return items;
return parentItems;
}
}

View File

@ -5,6 +5,7 @@ import {
ElectronMainApplication as TheiaElectronMainApplication,
ElectronMainApplicationContribution,
} from '@theia/core/lib/electron-main/electron-main-application';
import { ElectronMessagingContribution as TheiaElectronMessagingContribution } from '@theia/core/lib/electron-main/messaging/electron-messaging-contribution';
import { TheiaElectronWindow as DefaultTheiaElectronWindow } from '@theia/core/lib/electron-main/theia-electron-window';
import { ContainerModule } from '@theia/core/shared/inversify';
import {
@ -17,8 +18,8 @@ import { IsTempSketch } from '../node/is-temp-sketch';
import { IDEUpdaterImpl } from './ide-updater/ide-updater-impl';
import { ElectronMainApplication } from './theia/electron-main-application';
import { ElectronMainWindowServiceImpl } from './theia/electron-main-window-service';
import { ElectronMessagingContribution } from './theia/electron-messaging-contribution';
import { TheiaElectronWindow } from './theia/theia-electron-window';
import { ElectronNativeKeymap } from '@theia/core/lib/electron-main/electron-native-keymap';
export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(ElectronMainApplication).toSelf().inSingletonScope();
@ -60,7 +61,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(IsTempSketch).toSelf().inSingletonScope();
// https://github.com/eclipse-theia/theia/issues/11688
bind(ElectronNativeKeymap).toSelf().inSingletonScope();
bind(ElectronMainApplicationContribution).toService(ElectronNativeKeymap);
// Fix for cannot reload window: https://github.com/eclipse-theia/theia/issues/11600
bind(ElectronMessagingContribution).toSelf().inSingletonScope();
rebind(TheiaElectronMessagingContribution).toService(
ElectronMessagingContribution
);
});

View File

@ -0,0 +1,44 @@
import { ChannelMultiplexer } from '@theia/core/lib/common/message-rpc/channel';
import {
ElectronMessagingContribution as TheiaElectronMessagingContribution,
ElectronWebContentChannel,
} from '@theia/core/lib/electron-main/messaging/electron-messaging-contribution';
import { injectable } from '@theia/core/shared/inversify';
// Electron window cannot reload: https://github.com/eclipse-theia/theia/issues/11600
// This patch fixes it by removing the channel multiplexer from the cache.
// A related PR in Theia: https://github.com/eclipse-theia/theia/pull/11810
@injectable()
export class ElectronMessagingContribution extends TheiaElectronMessagingContribution {
// Based on: https://github.com/kittaakos/theia/commit/12dd318df589f1c48de2b58545912d8385919b22
protected override createWindowChannelData(sender: Electron.WebContents): {
channel: ElectronWebContentChannel;
multiplexer: ChannelMultiplexer;
} {
const mainChannel = this.createWindowMainChannel(sender);
const multiplexer = new ChannelMultiplexer(mainChannel);
multiplexer.onDidOpenChannel((openEvent) => {
const { channel, id } = openEvent;
if (this.channelHandlers.route(id, channel)) {
console.debug(`Opening channel for service path '${id}'.`);
channel.onClose(() =>
console.debug(`Closing channel on service path '${id}'.`)
);
}
});
// When refreshing the browser window.
sender.once('did-navigate', () => {
multiplexer.onUnderlyingChannelClose({ reason: 'Window was refreshed' });
this.windowChannelMultiplexer.delete(sender.id);
});
// When closing the browser window.
sender.once('destroyed', () => {
multiplexer.onUnderlyingChannelClose({ reason: 'Window was closed' });
this.windowChannelMultiplexer.delete(sender.id);
});
const data = { channel: mainChannel, multiplexer };
this.windowChannelMultiplexer.set(sender.id, data);
return data;
}
}

View File

@ -1,46 +1,16 @@
import { injectable } from '@theia/core/shared/inversify';
import { ipcMain, IpcMainEvent } from '@theia/electron/shared/electron';
import {
RELOAD_REQUESTED_SIGNAL,
StopReason,
} from '@theia/core/lib/electron-common/messaging/electron-messages';
import { TheiaElectronWindow as DefaultTheiaElectronWindow } from '@theia/core/lib/electron-main/theia-electron-window';
import { FileUri } from '@theia/core/lib/node';
import URI from '@theia/core/lib/common/uri';
import { createDisposableListener } from '@theia/core/lib/electron-main/event-utils';
import { TheiaElectronWindow as DefaultTheiaElectronWindow } from '@theia/core/lib/electron-main/theia-electron-window';
import { injectable } from '@theia/core/shared/inversify';
import { ipcMain, IpcMainEvent } from '@theia/electron/shared/electron';
import { StartupTask } from '../../electron-common/startup-task';
import { load } from './window';
@injectable()
export class TheiaElectronWindow extends DefaultTheiaElectronWindow {
protected override async handleStopRequest(
onSafeCallback: () => unknown,
reason: StopReason
): Promise<boolean> {
// Only confirm close to windows that have loaded our frontend.
// Both the windows's URL and the FS path of the `index.html` should be converted to the "same" format to be able to compare them. (#11226)
// Notes:
// - Windows: file:///C:/path/to/somewhere vs file:///c%3A/path/to/somewhere
// - macOS: file:///Applications/App%20Name.app/Contents vs /Applications/App Name.app/Contents
// This URL string comes from electron, we can expect that this is properly encoded URL. For example, a space is `%20`
const currentUrl = new URI(this.window.webContents.getURL()).toString();
// THEIA_FRONTEND_HTML_PATH is an FS path, we have to covert to an encoded URI string.
const frontendUri = FileUri.create(
this.globals.THEIA_FRONTEND_HTML_PATH
).toString();
const safeToClose =
!currentUrl.includes(frontendUri) || (await this.checkSafeToStop(reason));
if (safeToClose) {
try {
await onSafeCallback();
return true;
} catch (e) {
console.warn(`Request ${StopReason[reason]} failed.`, e);
}
}
return false;
}
protected override reload(tasks?: StartupTask[]): void {
this.handleStopRequest(() => {
this.applicationState = 'init';

View File

@ -100,8 +100,8 @@ import WebSocketProviderImpl from './web-socket/web-socket-provider-impl';
import { WebSocketProvider } from './web-socket/web-socket-provider';
import { ClangFormatter } from './clang-formatter';
import { FormatterPath } from '../common/protocol/formatter';
import { LocalizationBackendContribution } from './i18n/localization-backend-contribution';
import { LocalizationBackendContribution as TheiaLocalizationBackendContribution } from '@theia/core/lib/node/i18n/localization-backend-contribution';
import { HostedPluginLocalizationService } from './theia/plugin-ext/hosted-plugin-localization-service';
import { HostedPluginLocalizationService as TheiaHostedPluginLocalizationService } from '@theia/plugin-ext/lib/hosted/node/hosted-plugin-localization-service';
import { SurveyNotificationServiceImpl } from './survey-service-impl';
import {
SurveyNotificationService,
@ -360,9 +360,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(BackendApplicationContribution).toService(PlotterBackendContribution);
bind(ArduinoLocalizationContribution).toSelf().inSingletonScope();
bind(LocalizationContribution).toService(ArduinoLocalizationContribution);
bind(LocalizationBackendContribution).toSelf().inSingletonScope();
rebind(TheiaLocalizationBackendContribution).toService(
LocalizationBackendContribution
bind(HostedPluginLocalizationService).toSelf().inSingletonScope();
rebind(TheiaHostedPluginLocalizationService).toService(
HostedPluginLocalizationService
);
// Survey notification bindings

View File

@ -1,45 +0,0 @@
import * as express from 'express';
import { inject, injectable } from '@theia/core/shared/inversify';
import { LocalizationBackendContribution as TheiaLocalizationBackendContribution } from '@theia/core/lib/node/i18n/localization-backend-contribution';
import { PluginDeployer } from '@theia/plugin-ext/lib/common/plugin-protocol';
import { PluginDeployerImpl } from '@theia/plugin-ext/lib/main/node/plugin-deployer-impl';
import { Deferred } from '@theia/core/lib/common/promise-util';
@injectable()
export class LocalizationBackendContribution extends TheiaLocalizationBackendContribution {
@inject(PluginDeployer)
private readonly pluginDeployer: PluginDeployerImpl;
private readonly initialized = new Deferred<void>();
override async initialize(): Promise<void> {
this.pluginDeployer.onDidDeploy(() => {
this.initialized.resolve();
});
return super.initialize();
}
override configure(app: express.Application): void {
app.get('/i18n/:locale', async (req, res) => {
let locale = req.params.locale;
/*
Waiting for the deploy of the language plugins is necessary to avoid checking the available
languages before they're finished to be loaded: https://github.com/eclipse-theia/theia/issues/11471
*/
const start = performance.now();
await this.initialized.promise;
console.info(
'Waiting for the deploy of the language plugins took: ' +
(performance.now() - start) +
' ms.'
);
locale = this.localizationProvider
.getAvailableLanguages()
.some((e) => e.languageId === locale)
? locale
: 'en';
this.localizationProvider.setCurrentLanguage(locale);
res.send(this.localizationProvider.loadLocalization(locale));
});
}
}

View File

@ -12,7 +12,7 @@ import {
} from './cli-protocol/cc/arduino/cli/commands/v1/monitor_pb';
import { CoreClientAware } from './core-client-provider';
import { WebSocketProvider } from './web-socket/web-socket-provider';
import { Port as RpcPort } from 'arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/port_pb';
import { Port as RpcPort } from './cli-protocol/cc/arduino/cli/commands/v1/port_pb';
import {
MonitorSettings,
PluggableMonitorSettings,

View File

@ -1,6 +1,10 @@
import * as fs from 'fs';
import { join } from 'path';
import { injectable, inject, postConstruct } from 'inversify';
import {
injectable,
inject,
postConstruct,
} from '@theia/core/shared/inversify';
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
import { FileUri } from '@theia/core/lib/node/file-uri';
import { promisify } from 'util';
@ -95,7 +99,7 @@ export class MonitorSettingsProviderImpl implements MonitorSettingsProvider {
const rawJson = await promisify(fs.readFile)(
this.pluggableMonitorSettingsPath,
{
encoding: 'utf-8',
encoding: 'utf8',
flag: 'a+', // a+ = append and read, creating the file if it doesn't exist
}
);

View File

@ -1,5 +1,5 @@
import * as path from 'path';
import * as express from 'express';
import * as express from '@theia/core/shared/express';
import { injectable } from '@theia/core/shared/inversify';
import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';

View File

@ -0,0 +1,13 @@
import * as fs from '@theia/core/shared/fs-extra';
import { injectable } from '@theia/core/shared/inversify';
import { HostedPluginLocalizationService as TheiaHostedPluginLocalizationService } from '@theia/plugin-ext/lib/hosted/node/hosted-plugin-localization-service';
@injectable()
export class HostedPluginLocalizationService extends TheiaHostedPluginLocalizationService {
// Remove when https://github.com/eclipse-theia/theia/pull/11853 is available from Theia.
override async initialize(): Promise<void> {
this.getLocalizationCacheDir()
.then((cacheDir) => fs.emptyDir(cacheDir))
.then(() => this._ready.resolve());
}
}

View File

@ -71,7 +71,7 @@ namespace Chunks {
if (current && current.buffers) {
result.push([
current.severity,
Buffer.concat(current.buffers).toString('utf-8'),
Buffer.concat(current.buffers).toString('utf8'),
]);
}
};

View File

@ -1,6 +1,6 @@
import { Emitter } from '@theia/core';
import { injectable } from '@theia/core/shared/inversify';
import * as WebSocket from 'ws';
import * as WebSocket from '@theia/core/shared/ws';
import { WebSocketProvider } from './web-socket-provider';
@injectable()

View File

@ -1,5 +1,5 @@
import { Event } from '@theia/core/lib/common/event';
import * as WebSocket from 'ws';
import * as WebSocket from '@theia/core/shared/ws';
export const WebSocketProvider = Symbol('WebSocketProvider');
export interface WebSocketProvider {

View File

@ -1,7 +1,5 @@
{
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"noImplicitAny": true,
"noEmitOnError": true,
"noImplicitThis": true,

View File

@ -5,26 +5,26 @@
"license": "AGPL-3.0-or-later",
"main": "src-gen/frontend/electron-main.js",
"dependencies": {
"@theia/core": "1.25.0",
"@theia/debug": "1.25.0",
"@theia/editor": "1.25.0",
"@theia/electron": "1.25.0",
"@theia/file-search": "1.25.0",
"@theia/filesystem": "1.25.0",
"@theia/keymaps": "1.25.0",
"@theia/messages": "1.25.0",
"@theia/monaco": "1.25.0",
"@theia/navigator": "1.25.0",
"@theia/plugin-ext": "1.25.0",
"@theia/plugin-ext-vscode": "1.25.0",
"@theia/preferences": "1.25.0",
"@theia/process": "1.25.0",
"@theia/terminal": "1.25.0",
"@theia/workspace": "1.25.0",
"@theia/core": "1.31.1",
"@theia/debug": "1.31.1",
"@theia/editor": "1.31.1",
"@theia/electron": "1.31.1",
"@theia/file-search": "1.31.1",
"@theia/filesystem": "1.31.1",
"@theia/keymaps": "1.31.1",
"@theia/messages": "1.31.1",
"@theia/monaco": "1.31.1",
"@theia/navigator": "1.31.1",
"@theia/plugin-ext": "1.31.1",
"@theia/plugin-ext-vscode": "1.31.1",
"@theia/preferences": "1.31.1",
"@theia/process": "1.31.1",
"@theia/terminal": "1.31.1",
"@theia/workspace": "1.31.1",
"arduino-ide-extension": "2.0.3"
},
"devDependencies": {
"@theia/cli": "1.25.0",
"@theia/cli": "1.31.1",
"electron": "^15.3.5"
},
"scripts": {
@ -37,8 +37,13 @@
"frontend": {
"config": {
"applicationName": "Arduino IDE",
"defaultTheme": "arduino-theme",
"defaultTheme": {
"light": "arduino-theme",
"dark": "arduino-theme-dark"
},
"validatePreferencesSchema": false,
"preferences": {
"window.title": "${rootName}${activeEditorShort}${appName}",
"files.autoSave": "afterDelay",
"editor.minimap.enabled": false,
"editor.tabSize": 2,
@ -49,6 +54,7 @@
"strings": false
},
"editor.maxTokenizationLineLength": 500,
"editor.bracketPairColorization.enabled": false,
"breadcrumbs.enabled": false,
"workbench.tree.renderIndentGuides": "none",
"explorer.compactFolders": false

View File

@ -1,152 +0,0 @@
// Patch for the startup theme. Customizes the `ThemeService.get().defaultTheme();` to dispatch the default IDE2 theme based on the OS' theme.
// For all subsequent starts of the IDE the theme applied will be the last one set by the user.
// With the current version of Theia adopted (1.25) it is not possible to extend the `ThemeService`, it will be possible starting from Theia 1.27.
// Once the version of Theia is updated, this patch will be removed and this functionality will be implemented via dependency injection.
// Ideally, we should open a PR in Theia and add support for `light` and `dark` default themes in the app config.
const {
ThemeService,
ThemeServiceSymbol,
BuiltinThemeProvider,
} = require('@theia/core/lib/browser/theming');
const {
ApplicationProps,
} = require('@theia/application-package/lib/application-props');
const {
FrontendApplicationConfigProvider,
} = require('@theia/core/lib/browser/frontend-application-config-provider');
function fetchFrom(path) {
const { Endpoint } = require('@theia/core/lib/browser/endpoint');
const endpoint = new Endpoint({ path }).getRestUrl().toString();
return fetch(endpoint);
}
async function loadTranslations() {
const { nls } = require('@theia/core/lib/common/nls');
const defaultLocale = typeof window === 'object' && window && window.localStorage.getItem(nls.localeId) || '';
if (defaultLocale && !nls.locale) {
Object.assign(nls, {
locale: defaultLocale
});
}
if (nls.locale) {
const response = await fetchFrom(`/i18n/${nls.locale}`);
nls.localization = await response.json();
}
}
async function loadBackendOS() {
const response = await fetchFrom('/os');
const osType = await response.text();
const isWindows = osType === 'Windows';
const isOSX = osType === 'OSX';
OS.backend.isOSX = isOSX;
OS.backend.isWindows = isWindows;
OS.backend.type = () => osType;
}
function customizeMonacoNls() {
const MonacoNls = require('@theia/monaco-editor-core/esm/vs/nls');
const { nls: TheiaNls } = require('@theia/core/lib/common/nls');
const { Localization } = require('@theia/core/lib/common/i18n/localization');
Object.assign(MonacoNls, {
localize(_, label, ...args) {
if (TheiaNls.locale) {
const defaultKey = TheiaNls.getDefaultKey(label);
if (defaultKey) {
return TheiaNls.localize(defaultKey, label, ...args);
}
}
return Localization.format(label, args);
}
});
}
// It is a mighty hack to support theme updates in the bundled IDE2.
// If the custom theme registration happens before the restoration of the existing monaco themes, then any custom theme changes will be ignored.
// This patch introduces a static deferred promise in the monaco-theming service that will be resolved when the restoration is ready.
// IDE2 cannot require the monaco theme service on the outer module level, as it requires the application config provider to be initialized,
// but the initialization happens only in the generated `index.js`.
// This patch customizes the monaco theme service behavior before loading the DI containers via the preload.
// The preload is called only once before the app loads. The Theia extensions are not loaded at that point, but the app config provider is ready.
const preloader = require('@theia/core/lib/browser/preloader');
preloader.preload = async function () {
// Must require the monaco frontend module to activate the NLS customization for monaco.
// Otherwise, the NLS customization would trigger after the monaco UI components with all their translations are already loaded.
await Promise.allSettled([
loadTranslations(),
loadBackendOS(),
]);
customizeMonacoNls();
const { MonacoThemingService } = require('@theia/monaco/lib/browser/monaco-theming-service');
const { MonacoThemeServiceIsReady } = require('arduino-ide-extension/lib/browser/utils/window');
const { Deferred } = require('@theia/core/lib/common/promise-util');
const ready = new Deferred();
if (!window[MonacoThemeServiceIsReady]) {
window[MonacoThemeServiceIsReady] = ready;
console.log('Registered a custom monaco-theme service initialization signal on the window object.');
}
// Here, it is safe to patch the theme service, app config provider is ready.
MonacoThemingService.init = async function () {
this.updateBodyUiTheme();
ThemeService.get().onDidColorThemeChange(() => this.updateBodyUiTheme());
await this.restore();
ready.resolve();
}.bind(MonacoThemingService);
}.bind(preloader);
const lightTheme = 'arduino-theme';
const darkTheme = 'arduino-theme-dark';
const defaultTheme =
window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
? darkTheme
: lightTheme;
const originalGet = FrontendApplicationConfigProvider.get;
FrontendApplicationConfigProvider.get = function () {
const originalProps = originalGet.bind(FrontendApplicationConfigProvider)();
return { ...originalProps, defaultTheme };
}.bind(FrontendApplicationConfigProvider);
const arduinoDarkTheme = {
id: 'arduino-theme-dark',
type: 'dark',
label: 'Dark (Arduino)',
editorTheme: 'arduino-theme-dark',
activate() {},
deactivate() {},
};
const arduinoLightTheme = {
id: 'arduino-theme',
type: 'light',
label: 'Light (Arduino)',
editorTheme: 'arduino-theme',
activate() {},
deactivate() {},
};
if (!window[ThemeServiceSymbol]) {
const themeService = new ThemeService();
Object.defineProperty(themeService, 'defaultTheme', {
get: function () {
return (
this.themes[defaultTheme] ||
this.themes[ApplicationProps.DEFAULT.frontend.config.defaultTheme]
);
},
});
themeService.register(
...BuiltinThemeProvider.themes,
arduinoDarkTheme,
arduinoLightTheme
);
themeService.startupTheme();
themeService.setCurrentTheme(defaultTheme);
window[ThemeServiceSymbol] = themeService;
}
// Require the original, generated `index.js` for `webpack` as the next entry for the `bundle.js`.
require('../../src-gen/frontend/index');

View File

@ -1,26 +0,0 @@
/**
* This file can be edited to customize webpack configuration.
* To reset delete this file and rerun theia build again.
*/
// @ts-check
const config = require('./gen-webpack.config.js');
config.resolve.fallback['http'] = false;
config.resolve.fallback['fs'] = false;
/**
* Expose bundled modules on window.theia.moduleName namespace, e.g.
* window['theia']['@theia/core/lib/common/uri'].
* Such syntax can be used by external code, for instance, for testing.
config.module.rules.push({
test: /\.js$/,
loader: require.resolve('@theia/application-manager/lib/expose-loader')
}); */
// Load the patched `index.js` that sets the desired theme in IDE2 based on the OS' theme.
// The `patch/frontend/index.js` will require the original, generated `index.js`.
// See: https://github.com/arduino/arduino-ide/pull/1160.
config.entry.bundle = require('path').resolve(__dirname, 'patch/frontend/index.js');
module.exports = config;

2
electron/.gitignore vendored
View File

@ -6,6 +6,7 @@ working-copy/
src-gen/
node_modules/
build/yarn.lock
webpack.config.js
lib/
# The electron-builder output.
@ -19,4 +20,3 @@ build/package.json
# Resources the packager copies from dev to prod
build/resources/preload.html
build/patch/frontend/index.js

View File

@ -9,7 +9,7 @@
"node-log-rotate": "^0.1.5"
},
"devDependencies": {
"@theia/cli": "1.25.0",
"@theia/cli": "1.31.1",
"cross-env": "^7.0.2",
"electron-builder": "23.3.3",
"electron-notarize": "^1.1.1",

View File

@ -1,10 +0,0 @@
// @ts-check
const config = require('./gen-webpack.config.js');
const path = require('path');
// Load the patched `index.js` that sets the desired theme in IDE2 based on the OS' theme.
// The `patch/frontend/index.js` will require the original, generated `index.js`.
// See: https://github.com/arduino/arduino-ide/pull/1160.
config.entry.bundle = path.resolve(__dirname, 'patch/frontend/index.js');
module.exports = config;

View File

@ -42,251 +42,256 @@
);
}
//---------------------------+
// Clean the previous state. |
//---------------------------+
// rm -rf ../working-copy
rm('-rf', path('..', workingCopy));
// Clean up the `./electron/build` folder.
const resourcesToKeep = [
'patch',
'resources',
'scripts',
'template-package.json',
'webpack.config.js'
];
fs.readdirSync(path('..', 'build'))
.filter((filename) => resourcesToKeep.indexOf(filename) === -1)
.forEach((filename) => rm('-rf', path('..', 'build', filename)));
try {
//---------------------------+
// Clean the previous state. |
//---------------------------+
// rm -rf ../working-copy
rm('-rf', path('..', workingCopy));
// Clean up the `./electron/build` folder.
const resourcesToKeep = [
'patch',
'resources',
'scripts',
'template-package.json'
];
fs.readdirSync(path('..', 'build'))
.filter((filename) => resourcesToKeep.indexOf(filename) === -1)
.forEach((filename) => rm('-rf', path('..', 'build', filename)));
// Clean up the `./electron/build/patch` and `./electron/build/resources` folder with Git.
// To avoid file duplication between bundled app and dev mode, some files are copied from `./electron-app` to `./electron/build` folder.
const foldersToSyncFromDev = ['patch', 'resources'];
foldersToSyncFromDev.forEach(filename => shell.exec(`git -C ${path('..', 'build', filename)} clean -ffxdq`, { async: false }));
// Clean up the `./electron/build/resources` folder with Git.
// To avoid file duplication between bundled app and dev mode, some files are copied from `./electron-app` to `./electron/build` folder.
const foldersToSyncFromDev = ['resources'];
foldersToSyncFromDev.forEach((filename) =>
shell.exec(`git -C ${path('..', 'build', filename)} clean -ffxdq`, {
async: false,
})
);
const extensions = require('./extensions.json');
echo(
`Building the application with the following extensions:\n${extensions
.map((ext) => ` - ${ext}`)
.join(',\n')}`
);
const allDependencies = [...extensions, 'electron-app'];
const extensions = require('./extensions.json');
echo(
`Building the application with the following extensions:\n${extensions
.map((ext) => ` - ${ext}`)
.join(',\n')}`
);
const allDependencies = [...extensions, 'electron-app'];
//----------------------------------------------------------------------------------------------+
// Copy the following items into the `working-copy` folder. Make sure to reuse the `yarn.lock`. |
//----------------------------------------------------------------------------------------------+
mkdir('-p', path('..', workingCopy));
for (const filename of [
...allDependencies,
'yarn.lock',
'package.json',
'lerna.json',
'i18n'
]) {
cp('-rf', path(rootPath, filename), path('..', workingCopy));
}
//----------------------------------------------------------------------------------------------+
// Copy the following items into the `working-copy` folder. Make sure to reuse the `yarn.lock`. |
//----------------------------------------------------------------------------------------------+
mkdir('-p', path('..', workingCopy));
for (const filename of [
...allDependencies,
'yarn.lock',
'package.json',
'lerna.json',
'i18n',
]) {
cp('-rf', path(rootPath, filename), path('..', workingCopy));
}
//---------------------------------------------------------------------------------------------+
// Copy the patched `index.js` for the frontend, the Theia preload, etc. from `./electron-app` |
//---------------------------------------------------------------------------------------------+
for (const filename of foldersToSyncFromDev) {
cp('-rf', path('..', workingCopy, 'electron-app', filename), path('..', 'build'));
}
//---------------------------------------------------------------------------------------------+
// Copy the patched `index.js` for the frontend, the Theia preload, etc. from `./electron-app` |
//---------------------------------------------------------------------------------------------+
for (const filename of foldersToSyncFromDev) {
cp(
'-rf',
path('..', workingCopy, 'electron-app', filename),
path('..', 'build')
);
}
//----------------------------------------------+
// Sanity check: all versions must be the same. |
//----------------------------------------------+
verifyVersions(allDependencies);
//----------------------------------------------+
// Sanity check: all versions must be the same. |
//----------------------------------------------+
verifyVersions(allDependencies);
//----------------------------------------------------------------------+
// Use the nightly patch version if not a release but requires publish. |
//----------------------------------------------------------------------+
if (!isRelease) {
for (const dependency of allDependencies) {
const pkg = require(`../working-copy/${dependency}/package.json`);
pkg.version = version;
for (const dependency in pkg.dependencies) {
if (allDependencies.indexOf(dependency) !== -1) {
pkg.dependencies[dependency] = version;
//----------------------------------------------------------------------+
// Use the nightly patch version if not a release but requires publish. |
//----------------------------------------------------------------------+
if (!isRelease) {
for (const dependency of allDependencies) {
const pkg = require(`../working-copy/${dependency}/package.json`);
pkg.version = version;
for (const dependency in pkg.dependencies) {
if (allDependencies.indexOf(dependency) !== -1) {
pkg.dependencies[dependency] = version;
}
}
fs.writeFileSync(
path('..', workingCopy, dependency, 'package.json'),
JSON.stringify(pkg, null, 2)
);
}
fs.writeFileSync(
path('..', workingCopy, dependency, 'package.json'),
JSON.stringify(pkg, null, 2)
);
}
}
verifyVersions(allDependencies);
verifyVersions(allDependencies);
//---------------------------------------------------------------------------------------------------+
// Save some time: no need to build the projects that are not needed in final app. Currently unused. |
//---------------------------------------------------------------------------------------------------+
//@ts-ignore
const rootPackageJson = require('../working-copy/package.json');
const workspaces = rootPackageJson.workspaces;
// We cannot remove the `electron-app`. Otherwise, there is not way to collect the unused dependencies.
const dependenciesToRemove = [];
for (const dependencyToRemove of dependenciesToRemove) {
const index = workspaces.indexOf(dependencyToRemove);
if (index !== -1) {
workspaces.splice(index, 1);
//---------------------------------------------------------------------------------------------------+
// Save some time: no need to build the projects that are not needed in final app. Currently unused. |
//---------------------------------------------------------------------------------------------------+
//@ts-ignore
const rootPackageJson = require('../working-copy/package.json');
const workspaces = rootPackageJson.workspaces;
// We cannot remove the `electron-app`. Otherwise, there is not way to collect the unused dependencies.
const dependenciesToRemove = [];
for (const dependencyToRemove of dependenciesToRemove) {
const index = workspaces.indexOf(dependencyToRemove);
if (index !== -1) {
workspaces.splice(index, 1);
}
}
}
rootPackageJson.workspaces = workspaces;
fs.writeFileSync(
path('..', workingCopy, 'package.json'),
JSON.stringify(rootPackageJson, null, 2)
);
rootPackageJson.workspaces = workspaces;
fs.writeFileSync(
path('..', workingCopy, 'package.json'),
JSON.stringify(rootPackageJson, null, 2)
);
//-------------------------------------------------------------------------------------------------+
// Rebuild the extension with the copied `yarn.lock`. It is a must to use the same Theia versions. |
//-------------------------------------------------------------------------------------------------+
exec(
`yarn --network-timeout 1000000 --cwd ${path('..', workingCopy)}`,
`Building the ${productName} application`
);
//-------------------------------------------------------------------------------------------------+
// Rebuild the extension with the copied `yarn.lock`. It is a must to use the same Theia versions. |
//-------------------------------------------------------------------------------------------------+
exec(
`yarn --network-timeout 1000000 --cwd ${path('..', workingCopy)}`,
`Building the ${productName} application`
);
//-------------------------------------------------------------------------------------------------------------------------+
// Test the application. With this approach, we cannot publish test results to GH Actions but save 6-10 minutes per builds |
//-------------------------------------------------------------------------------------------------------------------------+
exec(
`yarn --network-timeout 1000000 --cwd ${path('..', workingCopy)} test`,
`Testing the ${productName} application`
);
//-------------------------------------------------------------------------------------------------------------------------+
// Test the application. With this approach, we cannot publish test results to GH Actions but save 6-10 minutes per builds |
//-------------------------------------------------------------------------------------------------------------------------+
exec(
`yarn --network-timeout 1000000 --cwd ${path('..', workingCopy)} test`,
`Testing the ${productName} application`
);
// Collect all unused dependencies by the backend. We have to remove them from the electron app.
// The `bundle.js` already contains everything we need for the frontend.
// We have to do it before changing the dependencies to `local-path`.
const unusedDependencies = await utils.collectUnusedDependencies(
'../working-copy/electron-app/'
);
//-------------------------------------------------------------------------------------------------------------+
// Change the regular NPM dependencies to `local-paths`, so that we can build them without any NPM registries. |
//-------------------------------------------------------------------------------------------------------------+
for (const extension of extensions) {
if (extension !== 'arduino-ide-extension') {
// Do not unlink self.
// @ts-ignore
rootPackageJson = require(`../working-copy/${extension}/package.json`);
// @ts-ignore
rootPackageJson.dependencies['arduino-ide-extension'] =
'file:../arduino-ide-extension';
fs.writeFileSync(
path('..', workingCopy, extension, 'package.json'),
JSON.stringify(rootPackageJson, null, 2)
);
//-------------------------------------------------------------------------------------------------------------+
// Change the regular NPM dependencies to `local-paths`, so that we can build them without any NPM registries. |
//-------------------------------------------------------------------------------------------------------------+
for (const extension of extensions) {
if (extension !== 'arduino-ide-extension') {
// Do not unlink self.
// @ts-ignore
rootPackageJson = require(`../working-copy/${extension}/package.json`);
// @ts-ignore
rootPackageJson.dependencies['arduino-ide-extension'] =
'file:../arduino-ide-extension';
fs.writeFileSync(
path('..', workingCopy, extension, 'package.json'),
JSON.stringify(rootPackageJson, null, 2)
);
}
}
}
//------------------------------------------------------------------------------------+
// Merge the `working-copy/package.json` with `electron/build/template-package.json`. |
//------------------------------------------------------------------------------------+
// @ts-ignore
const appPackageJson = require('../working-copy/electron-app/package.json');
template.build.files = [
...template.build.files,
...unusedDependencies.map((name) => `!node_modules/${name}`),
];
const dependencies = {};
for (const extension of extensions) {
dependencies[extension] = `file:../working-copy/${extension}`;
}
// @ts-ignore
appPackageJson.dependencies = { ...appPackageJson.dependencies, ...dependencies };
appPackageJson.devDependencies = { ...appPackageJson.devDependencies, ...template.devDependencies };
// Deep-merging the Theia application configuration.
// @ts-ignore
const theia = merge(appPackageJson.theia || {}, template.theia || {});
const content = {
...appPackageJson,
...template,
theia,
//------------------------------------------------------------------------------------+
// Merge the `working-copy/package.json` with `electron/build/template-package.json`. |
//------------------------------------------------------------------------------------+
// @ts-ignore
dependencies: appPackageJson.dependencies,
devDependencies: appPackageJson.devDependencies,
// VS Code extensions and the plugins folder is defined in the top level `package.json`. The template picks them up.
theiaPluginsDir: rootPackageJson.theiaPluginsDir,
theiaPlugins: rootPackageJson.theiaPlugins,
};
fs.writeFileSync(
path('..', 'build', 'package.json'),
JSON.stringify(
merge(content, template, { arrayMerge: (_, sourceArray) => sourceArray }),
null,
2
)
);
const appPackageJson = require('../working-copy/electron-app/package.json');
const dependencies = {};
for (const extension of extensions) {
dependencies[extension] = `file:../working-copy/${extension}`;
}
// @ts-ignore
appPackageJson.dependencies = {
...appPackageJson.dependencies,
...dependencies,
};
appPackageJson.devDependencies = {
...appPackageJson.devDependencies,
...template.devDependencies,
};
// Deep-merging the Theia application configuration.
// @ts-ignore
const theia = merge(appPackageJson.theia || {}, template.theia || {});
const content = {
...appPackageJson,
...template,
theia,
// @ts-ignore
dependencies: appPackageJson.dependencies,
devDependencies: appPackageJson.devDependencies,
// VS Code extensions and the plugins folder is defined in the top level `package.json`. The template picks them up.
theiaPluginsDir: rootPackageJson.theiaPluginsDir,
theiaPlugins: rootPackageJson.theiaPlugins,
};
fs.writeFileSync(
path('..', 'build', 'package.json'),
JSON.stringify(
merge(content, template, {
arrayMerge: (_, sourceArray) => sourceArray,
}),
null,
2
)
);
echo(`📜 Effective 'package.json' for the ${productName} application is:
echo(`📜 Effective 'package.json' for the ${productName} application is:
-----------------------
${fs.readFileSync(path('..', 'build', 'package.json')).toString()}
-----------------------
`);
// Make sure the original `yarn.lock` file is used from the electron application.
if (fs.existsSync(path('..', 'build', 'yarn.lock'))) {
echo(`${path('..', 'build', 'yarn.lock')} must not exist.`);
shell.exit(1);
}
cp('-rf', path(rootPath, 'yarn.lock'), path('..', 'build'));
if (!fs.existsSync(path('..', 'build', 'yarn.lock'))) {
echo(`${path('..', 'build', 'yarn.lock')} does not exist.`);
shell.exit(1);
}
//-------------------------------------------------------------------------------------------+
// Install all private and public dependencies for the electron application and build Theia. |
//-------------------------------------------------------------------------------------------+
exec(
`yarn --network-timeout 1000000 --cwd ${path('..', 'build')}`,
'Installing dependencies'
);
exec(
`yarn --network-timeout 1000000 --cwd ${path('..', 'build')} build`,
`Building the ${productName} application`
);
exec(
`yarn --network-timeout 1000000 --cwd ${path('..', 'build')} rebuild`,
'Rebuild native dependencies'
);
//------------------------------------------------------------------------------+
// Create a throw away dotenv file which we use to feed the builder with input. |
//------------------------------------------------------------------------------+
const dotenv = 'electron-builder.env';
if (fs.existsSync(path('..', 'build', dotenv))) {
rm('-rf', path('..', 'build', dotenv));
}
// For the releases we use the desired tag as is defined by `$(Release.Tag)` from Azure.
// For the preview builds we use the version from the `electron/build/package.json` with the short commit hash.
fs.writeFileSync(path('..', 'build', dotenv), `ARDUINO_VERSION=${version}`);
//-----------------------------------+
// Package the electron application. |
//-----------------------------------+
exec(
`yarn --network-timeout 1000000 --cwd ${path('..', 'build')} package`,
`Packaging your ${productName} application`
);
//-----------------------------------------------------------------------------------------------------+
// Recalculate artifacts hash and copy to another folder (because they can change after signing them).
// Azure does not support wildcard for `PublishBuildArtifacts@1.pathToPublish` |
//-----------------------------------------------------------------------------------------------------+
if (isCI) {
try {
await recalculateArtifactsHash();
await copyFilesToBuildArtifacts();
} catch (e) {
echo(JSON.stringify(e));
// Make sure the original `yarn.lock` file is used from the electron application.
if (fs.existsSync(path('..', 'build', 'yarn.lock'))) {
echo(`${path('..', 'build', 'yarn.lock')} must not exist.`);
shell.exit(1);
}
cp('-rf', path(rootPath, 'yarn.lock'), path('..', 'build'));
if (!fs.existsSync(path('..', 'build', 'yarn.lock'))) {
echo(`${path('..', 'build', 'yarn.lock')} does not exist.`);
shell.exit(1);
}
}
echo(`🎉 Success. Your application is at: ${path('..', 'build', 'dist')}`);
restore();
//-------------------------------------------------------------------------------------------+
// Install all private and public dependencies for the electron application and build Theia. |
//-------------------------------------------------------------------------------------------+
exec(
`yarn --network-timeout 1000000 --cwd ${path('..', 'build')}`,
'Installing dependencies'
);
exec(
`yarn --network-timeout 1000000 --cwd ${path('..', 'build')} build`,
`Building the ${productName} application`
);
exec(
`yarn --network-timeout 1000000 --cwd ${path('..', 'build')} rebuild`,
'Rebuild native dependencies'
);
//------------------------------------------------------------------------------+
// Create a throw away dotenv file which we use to feed the builder with input. |
//------------------------------------------------------------------------------+
const dotenv = 'electron-builder.env';
if (fs.existsSync(path('..', 'build', dotenv))) {
rm('-rf', path('..', 'build', dotenv));
}
// For the releases we use the desired tag as is defined by `$(Release.Tag)` from Azure.
// For the preview builds we use the version from the `electron/build/package.json` with the short commit hash.
fs.writeFileSync(path('..', 'build', dotenv), `ARDUINO_VERSION=${version}`);
//-----------------------------------+
// Package the electron application. |
//-----------------------------------+
exec(
`yarn --network-timeout 1000000 --cwd ${path('..', 'build')} package`,
`Packaging your ${productName} application`
);
//-----------------------------------------------------------------------------------------------------+
// Recalculate artifacts hash and copy to another folder (because they can change after signing them).
// Azure does not support wildcard for `PublishBuildArtifacts@1.pathToPublish` |
//-----------------------------------------------------------------------------------------------------+
if (isCI) {
try {
await recalculateArtifactsHash();
await copyFilesToBuildArtifacts();
} catch (e) {
echo(JSON.stringify(e));
shell.exit(1);
}
}
echo(`🎉 Success. Your application is at: ${path('..', 'build', 'dist')}`);
} finally {
restore();
}
//--------+
// Utils. |
@ -514,7 +519,8 @@ ${fs.readFileSync(path('..', 'build', 'package.json')).toString()}
if (expectedVersion) {
if (!versions.has(expectedVersion)) {
echo(
`Mismatching version configuration. Expected version was: '${expectedVersion}' actual was: '${Array.from(versions)[0]
`Mismatching version configuration. Expected version was: '${expectedVersion}' actual was: '${
Array.from(versions)[0]
}'.`
);
shell.exit(1);

View File

@ -20,7 +20,6 @@
"crypto": "^1.0.1",
"dateformat": "^3.0.3",
"deepmerge": "2.01",
"depcheck": "^0.9.2",
"file-type": "^14.1.4",
"glob": "^7.1.6",
"is-ci": "^2.0.0",

View File

@ -5,51 +5,8 @@ const zip = require('7zip-min');
const temp = require('temp');
const path = require('path');
const shell = require('shelljs');
const depcheck = require('depcheck');
const fromFile = require('file-type').fromFile;
/**
* Resolves to an array of `npm` package names that are declared in the `package.json` but **not** used by the project.
*/
function collectUnusedDependencies(pathToProject = process.cwd()) {
const p = path.isAbsolute(pathToProject)
? pathToProject
: path.resolve(process.cwd(), pathToProject);
console.log(`⏱️ >>> Collecting unused backend dependencies for ${p}...`);
return new Promise((resolve) => {
depcheck(
p,
{
ignoreDirs: ['frontend'],
parsers: {
'*.js': depcheck.parser.es6,
'*.jsx': depcheck.parser.jsx,
},
detectors: [
depcheck.detector.requireCallExpression,
depcheck.detector.importDeclaration,
],
specials: [depcheck.special.eslint, depcheck.special.webpack],
},
(unused) => {
const { dependencies } = unused;
if (dependencies && dependencies.length > 0) {
console.log(
`👌 <<< The following unused dependencies have been found: ${JSON.stringify(
dependencies,
null,
2
)}`
);
} else {
console.log('👌 <<< No unused dependencies have been found.');
}
resolve(dependencies);
}
);
});
}
/**
* `pathToZip` is a `path/to/your/app-name.zip`.
* If the `pathToZip` archive does not have a root directory with name `app-name`, it creates one, and move the content from the
@ -127,7 +84,7 @@ function unpack(what, where) {
reject(error);
return;
}
resolve();
resolve(undefined);
});
});
}
@ -139,7 +96,7 @@ function pack(what, where) {
reject(error);
return;
}
resolve();
resolve(undefined);
});
});
}
@ -212,7 +169,6 @@ function getChannelFile(platform) {
}
module.exports = {
collectUnusedDependencies,
adjustArchiveStructure,
isZip,
unpack,

View File

@ -14,112 +14,6 @@
dependencies:
"7zip-bin" "^5.0.3"
"@babel/code-frame@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e"
integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==
dependencies:
"@babel/highlight" "^7.8.3"
"@babel/generator@^7.9.0":
version "7.9.3"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.3.tgz#7c8b2956c6f68b3ab732bd16305916fbba521d94"
integrity sha512-RpxM252EYsz9qLUIq6F7YJyK1sv0wWDBFuztfDGWaQKzHjqDHysxSiRUpA/X9jmfqo+WzkAVKFaUily5h+gDCQ==
dependencies:
"@babel/types" "^7.9.0"
jsesc "^2.5.1"
lodash "^4.17.13"
source-map "^0.5.0"
"@babel/helper-function-name@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz#eeeb665a01b1f11068e9fb86ad56a1cb1a824cca"
integrity sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==
dependencies:
"@babel/helper-get-function-arity" "^7.8.3"
"@babel/template" "^7.8.3"
"@babel/types" "^7.8.3"
"@babel/helper-get-function-arity@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5"
integrity sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==
dependencies:
"@babel/types" "^7.8.3"
"@babel/helper-split-export-declaration@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9"
integrity sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==
dependencies:
"@babel/types" "^7.8.3"
"@babel/helper-validator-identifier@^7.9.0":
version "7.9.0"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz#ad53562a7fc29b3b9a91bbf7d10397fd146346ed"
integrity sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==
"@babel/highlight@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797"
integrity sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==
dependencies:
chalk "^2.0.0"
esutils "^2.0.2"
js-tokens "^4.0.0"
"@babel/parser@^7.7.7", "@babel/parser@^7.9.0":
version "7.9.3"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.3.tgz#043a5fc2ad8b7ea9facddc4e802a1f0f25da7255"
integrity sha512-E6SpIDJZ0cZAKoCNk+qSDd0ChfTnpiJN9FfNf3RZ20dzwA2vL2oq5IX1XTVT+4vDmRlta2nGk5HGMMskJAR+4A==
"@babel/parser@^7.8.3":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.4.tgz#d1dbe64691d60358a974295fa53da074dd2ce8e8"
integrity sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==
"@babel/template@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.3.tgz#e02ad04fe262a657809327f578056ca15fd4d1b8"
integrity sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==
dependencies:
"@babel/code-frame" "^7.8.3"
"@babel/parser" "^7.8.3"
"@babel/types" "^7.8.3"
"@babel/traverse@^7.7.4":
version "7.9.0"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.0.tgz#d3882c2830e513f4fe4cec9fe76ea1cc78747892"
integrity sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w==
dependencies:
"@babel/code-frame" "^7.8.3"
"@babel/generator" "^7.9.0"
"@babel/helper-function-name" "^7.8.3"
"@babel/helper-split-export-declaration" "^7.8.3"
"@babel/parser" "^7.9.0"
"@babel/types" "^7.9.0"
debug "^4.1.0"
globals "^11.1.0"
lodash "^4.17.13"
"@babel/types@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.3.tgz#5a383dffa5416db1b73dedffd311ffd0788fb31c"
integrity sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==
dependencies:
esutils "^2.0.2"
lodash "^4.17.13"
to-fast-properties "^2.0.0"
"@babel/types@^7.9.0":
version "7.9.0"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.0.tgz#00b064c3df83ad32b2dbf5ff07312b15c7f1efb5"
integrity sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==
dependencies:
"@babel/helper-validator-identifier" "^7.9.0"
lodash "^4.17.13"
to-fast-properties "^2.0.0"
"@sinonjs/commons@^1", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0":
version "1.7.1"
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.7.1.tgz#da5fd19a5f71177a53778073978873964f49acf1"
@ -161,11 +55,6 @@
resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.1.1.tgz#f0d92c12f87079ddfd1b29f614758b9696bc29e3"
integrity sha512-XO6INPbZCxdprl+9qa/AAbFFOMzzwqYxpjPgLICrMD6C2FCw6qfJOPcBk6JqqPLSaZ/Qx87qn4rpPmPMwaAK6w==
"@types/color-name@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
"@types/debug@^4.1.5":
version "4.1.5"
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd"
@ -210,11 +99,6 @@ ansi-regex@^4.1.0:
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
ansi-regex@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
ansi-styles@^3.2.0, ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
@ -222,14 +106,6 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1:
dependencies:
color-convert "^1.9.0"
ansi-styles@^4.0.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359"
integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==
dependencies:
"@types/color-name" "^1.1.1"
color-convert "^2.0.1"
anymatch@~3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142"
@ -280,31 +156,7 @@ browser-stdout@1.3.1:
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
builtin-modules@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.1.0.tgz#aad97c15131eb76b65b50ef208e7584cd76a7484"
integrity sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==
caller-callsite@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134"
integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=
dependencies:
callsites "^2.0.0"
caller-path@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4"
integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=
dependencies:
caller-callsite "^2.0.0"
callsites@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=
camelcase@^5.0.0, camelcase@^5.3.1:
camelcase@^5.0.0:
version "5.3.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
@ -321,7 +173,7 @@ chai@^4.2.0:
pathval "^1.1.0"
type-detect "^4.0.5"
chalk@^2.0.0, chalk@^2.4.2:
chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@ -373,15 +225,6 @@ cliui@^5.0.0:
strip-ansi "^5.2.0"
wrap-ansi "^5.1.0"
cliui@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==
dependencies:
string-width "^4.2.0"
strip-ansi "^6.0.0"
wrap-ansi "^6.2.0"
code-point-at@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
@ -394,38 +237,16 @@ color-convert@^1.9.0:
dependencies:
color-name "1.1.3"
color-convert@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
dependencies:
color-name "~1.1.4"
color-name@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
cosmiconfig@^5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a"
integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==
dependencies:
import-fresh "^2.0.0"
is-directory "^0.3.1"
js-yaml "^3.13.1"
parse-json "^4.0.0"
cross-spawn@^6.0.0:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
@ -447,11 +268,6 @@ dateformat@^3.0.3:
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
de-indent@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
integrity sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=
debug@3.2.6:
version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
@ -459,7 +275,7 @@ debug@3.2.6:
dependencies:
ms "^2.1.1"
debug@^4.1.0, debug@^4.1.1:
debug@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
@ -490,34 +306,6 @@ define-properties@^1.1.2, define-properties@^1.1.3:
dependencies:
object-keys "^1.0.12"
depcheck@^0.9.2:
version "0.9.2"
resolved "https://registry.yarnpkg.com/depcheck/-/depcheck-0.9.2.tgz#9e3198b44a527836914c61ba5395479c62ecbaf4"
integrity sha512-w5f+lSZqLJJkk58s44eOd0Vor7hLZot4PlFL0y2JsIX5LuHQ2eAjHlDVeGBD4Mj6ZQSKakvKWRRCcPlvrdU2Sg==
dependencies:
"@babel/parser" "^7.7.7"
"@babel/traverse" "^7.7.4"
builtin-modules "^3.0.0"
camelcase "^5.3.1"
cosmiconfig "^5.2.1"
debug "^4.1.1"
deps-regex "^0.1.4"
js-yaml "^3.4.2"
lodash "^4.17.15"
minimatch "^3.0.2"
node-sass-tilde-importer "^1.0.2"
please-upgrade-node "^3.2.0"
require-package-name "^2.0.1"
resolve "^1.14.1"
vue-template-compiler "^2.6.11"
walkdir "^0.4.1"
yargs "^15.0.2"
deps-regex@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/deps-regex/-/deps-regex-0.1.4.tgz#518667b7691460a5e7e0a341be76eb7ce8090184"
integrity sha1-UYZnt2kUYKXn4KNBvnbrfOgJAYQ=
diff@3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
@ -533,11 +321,6 @@ emoji-regex@^7.0.1:
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
emoji-regex@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
end-of-stream@^1.1.0:
version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
@ -545,13 +328,6 @@ end-of-stream@^1.1.0:
dependencies:
once "^1.4.0"
error-ex@^1.3.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
dependencies:
is-arrayish "^0.2.1"
es-abstract@^1.17.0-next.1:
version "1.17.5"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9"
@ -588,11 +364,6 @@ esprima@^4.0.0:
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
esutils@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
execa@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
@ -623,11 +394,6 @@ fill-range@^7.0.1:
dependencies:
to-regex-range "^5.0.1"
find-parent-dir@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/find-parent-dir/-/find-parent-dir-0.3.0.tgz#33c44b429ab2b2f0646299c5f9f718f376ff8d54"
integrity sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ=
find-up@3.0.0, find-up@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
@ -635,14 +401,6 @@ find-up@3.0.0, find-up@^3.0.0:
dependencies:
locate-path "^3.0.0"
find-up@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
dependencies:
locate-path "^5.0.0"
path-exists "^4.0.0"
flat@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.0.tgz#090bec8b05e39cba309747f1d588f04dbaf98db2"
@ -718,11 +476,6 @@ glob@^7.0.0, glob@^7.1.3, glob@^7.1.6:
once "^1.3.0"
path-is-absolute "^1.0.0"
globals@^11.1.0:
version "11.12.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
growl@1.10.5:
version "1.10.5"
resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
@ -750,7 +503,7 @@ has@^1.0.3:
dependencies:
function-bind "^1.1.1"
he@1.2.0, he@^1.1.0:
he@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
@ -760,14 +513,6 @@ ieee754@^1.1.13:
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
import-fresh@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546"
integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY=
dependencies:
caller-path "^2.0.0"
resolve-from "^3.0.0"
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
@ -791,11 +536,6 @@ invert-kv@^2.0.0:
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02"
integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==
is-arrayish@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
is-binary-path@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
@ -825,11 +565,6 @@ is-date-object@^1.0.1:
resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e"
integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==
is-directory@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1"
integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=
is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
@ -847,11 +582,6 @@ is-fullwidth-code-point@^2.0.0:
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
is-fullwidth-code-point@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
is-glob@^4.0.1, is-glob@~4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
@ -898,12 +628,7 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
js-yaml@3.13.1, js-yaml@^3.13.1, js-yaml@^3.4.2:
js-yaml@3.13.1:
version "3.13.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
@ -911,16 +636,6 @@ js-yaml@3.13.1, js-yaml@^3.13.1, js-yaml@^3.4.2:
argparse "^1.0.7"
esprima "^4.0.0"
jsesc@^2.5.1:
version "2.5.2"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
json-parse-better-errors@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
just-extend@^4.0.2:
version "4.1.0"
resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.1.0.tgz#7278a4027d889601640ee0ce0e5a00b992467da4"
@ -941,19 +656,12 @@ locate-path@^3.0.0:
p-locate "^3.0.0"
path-exists "^3.0.0"
locate-path@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
dependencies:
p-locate "^4.1.0"
lodash.get@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
lodash@^4.17.13, lodash@^4.17.15:
lodash@^4.17.15:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
@ -986,7 +694,7 @@ mimic-fn@^2.0.0:
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4:
minimatch@3.0.4, minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
@ -1069,13 +777,6 @@ node-environment-flags@1.0.6:
object.getownpropertydescriptors "^2.0.3"
semver "^5.7.0"
node-sass-tilde-importer@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/node-sass-tilde-importer/-/node-sass-tilde-importer-1.0.2.tgz#1a15105c153f648323b4347693fdb0f331bad1ce"
integrity sha512-Swcmr38Y7uB78itQeBm3mThjxBy9/Ah/ykPIaURY/L6Nec9AyRoL/jJ7ECfMR+oZeCTVQNxVMu/aHU+TLRVbdg==
dependencies:
find-parent-dir "^0.3.0"
normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
@ -1152,7 +853,7 @@ p-is-promise@^2.0.0:
resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e"
integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==
p-limit@^2.0.0, p-limit@^2.2.0:
p-limit@^2.0.0:
version "2.2.2"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e"
integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==
@ -1166,36 +867,16 @@ p-locate@^3.0.0:
dependencies:
p-limit "^2.0.0"
p-locate@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
dependencies:
p-limit "^2.2.0"
p-try@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
parse-json@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=
dependencies:
error-ex "^1.3.1"
json-parse-better-errors "^1.0.1"
path-exists@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
path-exists@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
@ -1233,13 +914,6 @@ picomatch@^2.0.4:
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
please-upgrade-node@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942"
integrity sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==
dependencies:
semver-compare "^1.0.0"
pump@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
@ -1282,17 +956,7 @@ require-main-filename@^2.0.0:
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
require-package-name@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/require-package-name/-/require-package-name-2.0.1.tgz#c11e97276b65b8e2923f75dabf5fb2ef0c3841b9"
integrity sha1-wR6XJ2tluOKSP3Xav1+y7ww4Qbk=
resolve-from@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
integrity sha1-six699nWiBvItuZTM17rywoYh0g=
resolve@^1.1.6, resolve@^1.14.1:
resolve@^1.1.6:
version "1.15.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8"
integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==
@ -1306,11 +970,6 @@ rimraf@~2.6.2:
dependencies:
glob "^7.1.3"
semver-compare@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w=
semver@^5.5.0, semver@^5.7.0:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
@ -1365,11 +1024,6 @@ sinon@^9.0.1:
nise "^4.0.1"
supports-color "^7.1.0"
source-map@^0.5.0:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
@ -1401,15 +1055,6 @@ string-width@^3.0.0, string-width@^3.1.0:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^5.1.0"
string-width@^4.1.0, string-width@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5"
integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.0"
string.prototype.trimleft@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74"
@ -1447,13 +1092,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
dependencies:
ansi-regex "^4.1.0"
strip-ansi@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532"
integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==
dependencies:
ansi-regex "^5.0.0"
strip-eof@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
@ -1502,11 +1140,6 @@ temp@^0.9.1:
dependencies:
rimraf "~2.6.2"
to-fast-properties@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
to-regex-range@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
@ -1534,19 +1167,6 @@ typedarray-to-buffer@^3.1.5:
dependencies:
is-typedarray "^1.0.0"
vue-template-compiler@^2.6.11:
version "2.6.11"
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz#c04704ef8f498b153130018993e56309d4698080"
integrity sha512-KIq15bvQDrcCjpGjrAhx4mUlyyHfdmTaoNfeoATHLAiWB+MU3cx4lOzMwrnUh9cCxy0Lt1T11hAFY6TQgroUAA==
dependencies:
de-indent "^1.0.2"
he "^1.1.0"
walkdir@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.4.1.tgz#dc119f83f4421df52e3061e514228a2db20afa39"
integrity sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==
which-module@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
@ -1583,15 +1203,6 @@ wrap-ansi@^5.1.0:
string-width "^3.0.0"
strip-ansi "^5.0.0"
wrap-ansi@^6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
@ -1623,14 +1234,6 @@ yargs-parser@^11.1.1:
camelcase "^5.0.0"
decamelize "^1.2.0"
yargs-parser@^18.1.1:
version "18.1.1"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.1.tgz#bf7407b915427fc760fcbbccc6c82b4f0ffcbd37"
integrity sha512-KRHEsOM16IX7XuLnMOqImcPNbLVXMNHYAoFc3BKR8Ortl5gzDbtXvvEoGx9imk5E+X1VeNKNlcHr8B8vi+7ipA==
dependencies:
camelcase "^5.0.0"
decamelize "^1.2.0"
yargs-unparser@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f"
@ -1673,20 +1276,3 @@ yargs@^12.0.5:
which-module "^2.0.0"
y18n "^3.2.1 || ^4.0.0"
yargs-parser "^11.1.1"
yargs@^15.0.2:
version "15.3.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b"
integrity sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==
dependencies:
cliui "^6.0.0"
decamelize "^1.2.0"
find-up "^4.1.0"
get-caller-file "^2.0.1"
require-directory "^2.1.1"
require-main-filename "^2.0.0"
set-blocking "^2.0.0"
string-width "^4.2.0"
which-module "^2.0.0"
y18n "^4.0.0"
yargs-parser "^18.1.1"

View File

@ -450,11 +450,6 @@
"offline": "Offline",
"quitTitle": "Are you sure you want to quit?"
},
"debug": {
"start": "Start...",
"startError": "There was an error starting the debug session, check the logs for more details.",
"typeNotSupported": "The debug session type \"{0}\" is not supported."
},
"editor": {
"unsavedTitle": "Unsaved {0}"
},

View File

@ -10,9 +10,9 @@
"node": ">=16.0.0 <17"
},
"devDependencies": {
"@theia/cli": "1.25.0",
"@types/sinon": "^2.3.5",
"@theia/cli": "1.31.1",
"@types/jsdom": "^11.0.4",
"@types/sinon": "^2.3.5",
"@typescript-eslint/eslint-plugin": "^4.27.0",
"@typescript-eslint/parser": "^4.27.0",
"cross-env": "^7.0.2",
@ -24,6 +24,7 @@
"eslint-plugin-unused-imports": "^1.1.1",
"husky": "^6.0.0",
"ignore-styles": "^5.0.1",
"jsdom": "^11.5.1",
"lerna": "^3.20.2",
"lint-staged": "^11.0.0",
"node-gyp": "^9.3.0",
@ -31,13 +32,13 @@
"reflect-metadata": "^0.1.10",
"rimraf": "^2.6.1",
"semver": "^7.3.2",
"typescript": "~4.5.5",
"jsdom": "^11.5.1",
"style-dictionary": "^3.7.0",
"typescript": "~4.5.5",
"xhr2": "^0.2.1"
},
"resolutions": {
"@types/react": "16.14.25",
"@types/react": "18.0.0",
"@types/react-dom": "18.0.0",
"electron-rebuild": "3.2.9"
},
"scripts": {

26
scripts/sort-dependencies Executable file
View File

@ -0,0 +1,26 @@
#!/usr/bin/env node
// Sorts the dependencies and devDependencies in the package.json without yarn.
// Usage `./scripts/sort-dependencies ./path/to/package.json`
const { promises: fs } = require('fs');
const { join, isAbsolute } = require('path');
const arg = process.argv.slice(2).pop();
const path = isAbsolute(arg) ? arg : join(process.cwd(), arg);
fs.readFile(path, { encoding: 'utf8' }).then((raw) => {
const json = JSON.parse(raw);
['dependencies', 'devDependencies'].forEach((prop) => {
const value = json[prop];
if (value) {
json[prop] = Array.from(Object.entries(value))
.sort(([left], [right]) => left.localeCompare(right))
.reduce((acc, [key, value]) => {
acc[key] = value;
return acc;
}, {});
}
});
fs.writeFile(path, JSON.stringify(json, null, 2) + '\n', {
encoding: 'utf8',
});
});

4464
yarn.lock

File diff suppressed because it is too large Load Diff