mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-07-13 14:26:37 +00:00
Merge pull request #58 from bcmi-labs/use-sketch-as-ws
Use sketch folder as workspace
This commit is contained in:
commit
206b65f138
3
.gitignore
vendored
3
.gitignore
vendored
@ -6,7 +6,8 @@ build/
|
|||||||
downloads/
|
downloads/
|
||||||
!electron/build/
|
!electron/build/
|
||||||
src-gen/
|
src-gen/
|
||||||
arduino-ide-*/webpack.config.js
|
browser-app/webpack.config.js
|
||||||
|
electron-app/webpack.config.js
|
||||||
.DS_Store
|
.DS_Store
|
||||||
/workspace/static
|
/workspace/static
|
||||||
# switching from `electron` to `browser` in dev mode.
|
# switching from `electron` to `browser` in dev mode.
|
||||||
|
@ -8,7 +8,7 @@ ports:
|
|||||||
tasks:
|
tasks:
|
||||||
- init: >
|
- init: >
|
||||||
yarn &&
|
yarn &&
|
||||||
yarn --cwd ./arduino-ide-browser start
|
yarn --cwd ./browser-app start
|
||||||
|
|
||||||
github:
|
github:
|
||||||
prebuilds:
|
prebuilds:
|
||||||
|
6
.vscode/launch.json
vendored
6
.vscode/launch.json
vendored
@ -14,7 +14,7 @@
|
|||||||
"type": "node",
|
"type": "node",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"name": "Launch Backend",
|
"name": "Launch Backend",
|
||||||
"program": "${workspaceRoot}/arduino-ide-browser/src-gen/backend/main.js",
|
"program": "${workspaceRoot}/browser-app/src-gen/backend/main.js",
|
||||||
"args": [
|
"args": [
|
||||||
"--hostname=0.0.0.0",
|
"--hostname=0.0.0.0",
|
||||||
"--port=3000",
|
"--port=3000",
|
||||||
@ -26,8 +26,8 @@
|
|||||||
},
|
},
|
||||||
"sourceMaps": true,
|
"sourceMaps": true,
|
||||||
"outFiles": [
|
"outFiles": [
|
||||||
"${workspaceRoot}/arduino-ide-browser/src-gen/backend/*.js",
|
"${workspaceRoot}/browser-app/src-gen/backend/*.js",
|
||||||
"${workspaceRoot}/arduino-ide-browser/lib/**/*.js",
|
"${workspaceRoot}/browser-app/lib/**/*.js",
|
||||||
"${workspaceRoot}/arduino-ide-extension/*/lib/**/*.js"
|
"${workspaceRoot}/arduino-ide-extension/*/lib/**/*.js"
|
||||||
],
|
],
|
||||||
"smartStep": true,
|
"smartStep": true,
|
||||||
|
16
.vscode/tasks.json
vendored
16
.vscode/tasks.json
vendored
@ -4,9 +4,9 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"tasks": [
|
"tasks": [
|
||||||
{
|
{
|
||||||
"label": "Arduino-PoC - Start Browser Example",
|
"label": "Arduino Editor - Start Browser Example",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "yarn --cwd ./arduino-ide-browser start",
|
"command": "yarn --cwd ./browser-app start",
|
||||||
"group": "build",
|
"group": "build",
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"reveal": "always",
|
"reveal": "always",
|
||||||
@ -15,7 +15,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Arduino-PoC - Watch Theia Extension",
|
"label": "Arduino Editor - Watch Theia Extension",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "yarn --cwd ./arduino-ide-extension watch",
|
"command": "yarn --cwd ./arduino-ide-extension watch",
|
||||||
"group": "build",
|
"group": "build",
|
||||||
@ -26,9 +26,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Arduino-PoC - Watch Browser Example",
|
"label": "Arduino Editor - Watch Browser Example",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "yarn --cwd ./arduino-ide-browser watch",
|
"command": "yarn --cwd ./browser-app watch",
|
||||||
"group": "build",
|
"group": "build",
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"reveal": "always",
|
"reveal": "always",
|
||||||
@ -37,11 +37,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Arduino-PoC - Watch All",
|
"label": "Arduino Editor - Watch All",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"dependsOn": [
|
"dependsOn": [
|
||||||
"Arduino-PoC - Watch Theia Extension",
|
"Arduino Editor - Watch Theia Extension",
|
||||||
"Arduino-PoC - Watch Browser Example"
|
"Arduino Editor - Watch Browser Example"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -17,7 +17,7 @@ git clone https://github.com/bcmi-labs/arduino-editor
|
|||||||
cd arduino-editor
|
cd arduino-editor
|
||||||
yarn
|
yarn
|
||||||
yarn rebuild:electron
|
yarn rebuild:electron
|
||||||
yarn --cwd arduino-ide-electron start
|
yarn --cwd electron-app start
|
||||||
```
|
```
|
||||||
|
|
||||||
If you want to switch back to the browser-based example, execute the following in the repository root
|
If you want to switch back to the browser-based example, execute the following in the repository root
|
||||||
@ -26,7 +26,7 @@ yarn rebuild:browser
|
|||||||
```
|
```
|
||||||
Then you can start the browser example again:
|
Then you can start the browser example again:
|
||||||
```
|
```
|
||||||
yarn --cwd arduino-ide-browser start
|
yarn --cwd browser-app start
|
||||||
```
|
```
|
||||||
|
|
||||||
## Arduino-PoC Electron Application
|
## Arduino-PoC Electron Application
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
"@theia/core": "next",
|
"@theia/core": "next",
|
||||||
"@theia/editor": "next",
|
"@theia/editor": "next",
|
||||||
"@theia/filesystem": "next",
|
"@theia/filesystem": "next",
|
||||||
|
"@theia/git": "next",
|
||||||
"@theia/languages": "next",
|
"@theia/languages": "next",
|
||||||
"@theia/markers": "next",
|
"@theia/markers": "next",
|
||||||
"@theia/monaco": "next",
|
"@theia/monaco": "next",
|
||||||
@ -19,7 +20,6 @@
|
|||||||
"@theia/workspace": "next",
|
"@theia/workspace": "next",
|
||||||
"@theia/navigator": "next",
|
"@theia/navigator": "next",
|
||||||
"@theia/terminal": "next",
|
"@theia/terminal": "next",
|
||||||
"@theia/git": "next",
|
|
||||||
"@theia/search-in-workspace": "next",
|
"@theia/search-in-workspace": "next",
|
||||||
"@types/ps-tree": "^1.1.0",
|
"@types/ps-tree": "^1.1.0",
|
||||||
"@types/which": "^1.3.1",
|
"@types/which": "^1.3.1",
|
||||||
@ -40,11 +40,12 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"decompress": "^4.2.0",
|
"decompress": "^4.2.0",
|
||||||
"decompress-tarbz2": "^4.1.1",
|
"decompress-targz": "^4.1.1",
|
||||||
"decompress-unzip": "^4.0.1",
|
"decompress-unzip": "^4.0.1",
|
||||||
"download": "^7.1.0",
|
"download": "^7.1.0",
|
||||||
"grpc-tools": "^1.7.3",
|
"grpc-tools": "^1.7.3",
|
||||||
"grpc_tools_node_protoc_ts": "^2.5.0",
|
"grpc_tools_node_protoc_ts": "^2.5.0",
|
||||||
|
"moment": "^2.24.0",
|
||||||
"ncp": "^2.0.0",
|
"ncp": "^2.0.0",
|
||||||
"rimraf": "^2.6.1",
|
"rimraf": "^2.6.1",
|
||||||
"shelljs": "^0.8.3",
|
"shelljs": "^0.8.3",
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
// The links to the downloads as of today (11.08.) are the followings:
|
// The links to the downloads as of today (02.09.) are the followings:
|
||||||
// - https://downloads.arduino.cc/arduino-cli/nightly/arduino-cli-nightly-latest-${FILE_NAME}
|
// In order to get the latest nightly build for your platform use the following links replacing <DATE> with the current date, using the format YYYYMMDD (i.e for 2019/Aug/06 use 20190806 )
|
||||||
// - https://downloads.arduino.cc/arduino-cli/arduino-cli-latest-${FILE_NAME}
|
// Linux 64 bit: https://downloads.arduino.cc/arduino-cli/nightly/arduino-cli_nightly-<DATE>_Linux_64bit.tar.gz
|
||||||
|
// Linux ARM 64 bit: https://downloads.arduino.cc/arduino-cli/nightly/arduino-cli_nightly-<DATE>_Linux_ARM64.tar.gz
|
||||||
|
// Windows 64 bit: https://downloads.arduino.cc/arduino-cli/nightly/arduino-cli_nightly-<DATE>_Windows_64bit.zip
|
||||||
|
// Mac OSX: https://downloads.arduino.cc/arduino-cli/nightly/arduino-cli_nightly-<DATE>_macOS_64bit.tar.gz
|
||||||
|
// [...]
|
||||||
|
// redirecting to latest generated builds by replacing latest with the latest available build date, using the format YYYYMMDD (i.e for 2019/Aug/06 latest is replaced with 20190806
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
|
|
||||||
const DEFAULT_VERSION = 'nightly';
|
const DEFAULT_VERSION = 'latest'; // require('moment')().format('YYYYMMDD');
|
||||||
|
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
@ -14,7 +19,7 @@
|
|||||||
const download = require('download');
|
const download = require('download');
|
||||||
const decompress = require('decompress');
|
const decompress = require('decompress');
|
||||||
const unzip = require('decompress-unzip');
|
const unzip = require('decompress-unzip');
|
||||||
const untarbz = require('decompress-tarbz2');
|
const untargz = require('decompress-targz');
|
||||||
|
|
||||||
process.on('unhandledRejection', (reason, _) => {
|
process.on('unhandledRejection', (reason, _) => {
|
||||||
shell.echo(String(reason));
|
shell.echo(String(reason));
|
||||||
@ -31,11 +36,7 @@
|
|||||||
.option('cli-version', {
|
.option('cli-version', {
|
||||||
alias: 'cv',
|
alias: 'cv',
|
||||||
default: DEFAULT_VERSION,
|
default: DEFAULT_VERSION,
|
||||||
choices: [
|
describe: `The version of the 'arduino-cli' to download with the YYYYMMDD format, or 'latest'. Defaults to ${DEFAULT_VERSION}.`
|
||||||
// 'latest', // TODO: How do we get the source for `latest`. Currently, `latest` is the `0.3.7-alpha.preview`.
|
|
||||||
'nightly'
|
|
||||||
],
|
|
||||||
describe: `The version of the 'arduino-cli' to download. Defaults to ${DEFAULT_VERSION}.`
|
|
||||||
})
|
})
|
||||||
.option('force-download', {
|
.option('force-download', {
|
||||||
alias: 'fd',
|
alias: 'fd',
|
||||||
@ -68,13 +69,12 @@
|
|||||||
|
|
||||||
const suffix = (() => {
|
const suffix = (() => {
|
||||||
switch (platform) {
|
switch (platform) {
|
||||||
case 'darwin': return 'macosx.zip';
|
case 'darwin': return 'macOS_64bit.tar.gz';
|
||||||
case 'win32': return 'windows.zip';
|
case 'win32': return 'Windows_64bit.zip';
|
||||||
case 'linux': {
|
case 'linux': {
|
||||||
switch (arch) {
|
switch (arch) {
|
||||||
case 'arm64': return 'linuxarm.tar.bz2';
|
case 'arm64': return 'Linux_ARM64.tar.gz';
|
||||||
case 'x32': return 'linux32.tar.bz2';
|
case 'x64': return 'Linux_64bit.tar.gz';
|
||||||
case 'x64': return 'linux64.tar.bz2';
|
|
||||||
default: return undefined;
|
default: return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,7 +86,7 @@
|
|||||||
shell.exit(1);
|
shell.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = `https://downloads.arduino.cc/arduino-cli/${version === 'nightly' ? 'nightly/' : ''}arduino-cli-${version}-latest-${suffix}`;
|
const url = `https://downloads.arduino.cc/arduino-cli/nightly/arduino-cli_nightly-${version}_${suffix}`;
|
||||||
shell.echo(`>>> Downloading 'arduino-cli' from '${url}'...`);
|
shell.echo(`>>> Downloading 'arduino-cli' from '${url}'...`);
|
||||||
const data = await download(url);
|
const data = await download(url);
|
||||||
shell.echo(`<<< Download succeeded.`);
|
shell.echo(`<<< Download succeeded.`);
|
||||||
@ -94,16 +94,21 @@
|
|||||||
const files = await decompress(data, downloads, {
|
const files = await decompress(data, downloads, {
|
||||||
plugins: [
|
plugins: [
|
||||||
unzip(),
|
unzip(),
|
||||||
untarbz()
|
untargz()
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
shell.echo('<<< Decompressing succeeded.');
|
if (files.length === 0) {
|
||||||
|
|
||||||
if (files.length !== 1) {
|
|
||||||
shell.echo('Error ocurred when decompressing the CLI.');
|
shell.echo('Error ocurred when decompressing the CLI.');
|
||||||
shell.exit(1);
|
shell.exit(1);
|
||||||
}
|
}
|
||||||
if (shell.mv('-f', path.join(downloads, files[0].path), cli).code !== 0) {
|
const cliIndex = files.findIndex(f => f.path.startsWith('arduino-cli'));
|
||||||
|
if (cliIndex === -1) {
|
||||||
|
shell.echo('The downloaded artifact does not contains the CLI.');
|
||||||
|
shell.exit(1);
|
||||||
|
}
|
||||||
|
shell.echo('<<< Decompressing succeeded.');
|
||||||
|
|
||||||
|
if (shell.mv('-f', path.join(downloads, files[cliIndex].path), cli).code !== 0) {
|
||||||
shell.echo(`Could not move file to ${cli}.`);
|
shell.echo(`Could not move file to ${cli}.`);
|
||||||
shell.exit(1);
|
shell.exit(1);
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,7 @@ import { BoardsConfigDialog } from './boards/boards-config-dialog';
|
|||||||
import { BoardsToolBarItem } from './boards/boards-toolbar-item';
|
import { BoardsToolBarItem } from './boards/boards-toolbar-item';
|
||||||
import { BoardsConfig } from './boards/boards-config';
|
import { BoardsConfig } from './boards/boards-config';
|
||||||
import { MonitorService } from '../common/protocol/monitor-service';
|
import { MonitorService } from '../common/protocol/monitor-service';
|
||||||
|
import { ConfigService } from '../common/protocol/config-service';
|
||||||
|
|
||||||
export namespace ArduinoMenus {
|
export namespace ArduinoMenus {
|
||||||
export const SKETCH = [...MAIN_MENU_BAR, '3_sketch'];
|
export const SKETCH = [...MAIN_MENU_BAR, '3_sketch'];
|
||||||
@ -133,21 +134,19 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
|||||||
|
|
||||||
@inject(LabelProvider)
|
@inject(LabelProvider)
|
||||||
protected readonly labelProvider: LabelProvider;
|
protected readonly labelProvider: LabelProvider;
|
||||||
|
|
||||||
@inject(QuickOpenService)
|
@inject(QuickOpenService)
|
||||||
protected readonly quickOpenService: QuickOpenService;
|
protected readonly quickOpenService: QuickOpenService;
|
||||||
|
|
||||||
|
@inject(WorkspaceService)
|
||||||
|
protected readonly workspaceService: WorkspaceService;
|
||||||
|
|
||||||
|
@inject(ConfigService)
|
||||||
|
protected readonly configService: ConfigService;
|
||||||
|
|
||||||
protected boardsToolbarItem: BoardsToolBarItem | null;
|
protected boardsToolbarItem: BoardsToolBarItem | null;
|
||||||
protected wsSketchCount: number = 0;
|
protected wsSketchCount: number = 0;
|
||||||
|
|
||||||
constructor(@inject(WorkspaceService) protected readonly workspaceService: WorkspaceService) {
|
|
||||||
this.workspaceService.onWorkspaceChanged(() => {
|
|
||||||
if (this.workspaceService.workspace) {
|
|
||||||
this.registerSketchesInMenu(this.menuRegistry);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
@postConstruct()
|
@postConstruct()
|
||||||
protected async init(): Promise<void> {
|
protected async init(): Promise<void> {
|
||||||
// This is a hack. Otherwise, the backend services won't bind.
|
// This is a hack. Otherwise, the backend services won't bind.
|
||||||
@ -161,6 +160,8 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
|||||||
}
|
}
|
||||||
this.boardsServiceClient.onBoardsConfigChanged(updateStatusBar);
|
this.boardsServiceClient.onBoardsConfigChanged(updateStatusBar);
|
||||||
updateStatusBar(this.boardsServiceClient.boardsConfig);
|
updateStatusBar(this.boardsServiceClient.boardsConfig);
|
||||||
|
|
||||||
|
this.registerSketchesInMenu(this.menuRegistry);
|
||||||
}
|
}
|
||||||
|
|
||||||
registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
||||||
@ -453,7 +454,13 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async getWorkspaceSketches(): Promise<Sketch[]> {
|
protected async getWorkspaceSketches(): Promise<Sketch[]> {
|
||||||
const sketches = this.sketches.getSketches(this.workspaceService.workspace);
|
|
||||||
|
let sketches: Sketch[] = [];
|
||||||
|
const config = await this.configService.getConfiguration();
|
||||||
|
const stat = await this.fileSystem.getFileStat(config.sketchDirUri);
|
||||||
|
if (!!stat) {
|
||||||
|
sketches = await this.sketches.getSketches(stat);
|
||||||
|
}
|
||||||
return sketches;
|
return sketches;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -461,13 +468,18 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
|||||||
const url = new URL(window.location.href);
|
const url = new URL(window.location.href);
|
||||||
const currentSketch = url.searchParams.get('sketch');
|
const currentSketch = url.searchParams.get('sketch');
|
||||||
// Nothing to do if we want to open the same sketch which is already opened.
|
// Nothing to do if we want to open the same sketch which is already opened.
|
||||||
if (!!currentSketch && new URI(currentSketch).toString() === new URI(uri).toString()) {
|
const sketchUri = new URI(uri);
|
||||||
this.messageService.info(`The '${this.labelProvider.getLongName(new URI(uri))}' is already opened.`);
|
if (!!currentSketch && new URI(currentSketch).toString() === sketchUri.toString()) {
|
||||||
|
this.messageService.info(`The '${this.labelProvider.getLongName(sketchUri)}' is already opened.`);
|
||||||
// NOOP.
|
// NOOP.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Preserve the current window if the `sketch` is not in the `searchParams`.
|
// Preserve the current window if the `sketch` is not in the `searchParams`.
|
||||||
url.searchParams.set('sketch', uri);
|
url.searchParams.set('sketch', uri);
|
||||||
|
const hash = await this.fileSystem.getFsPath(sketchUri.toString());
|
||||||
|
if (hash) {
|
||||||
|
url.hash = hash;
|
||||||
|
}
|
||||||
if (!currentSketch) {
|
if (!currentSketch) {
|
||||||
setTimeout(() => window.location.href = url.toString(), 100);
|
setTimeout(() => window.location.href = url.toString(), 100);
|
||||||
return;
|
return;
|
||||||
@ -543,25 +555,6 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// private async onNoBoardsInstalled() {
|
|
||||||
// const action = await this.messageService.info("You have no boards installed. Use the boards manager to install one.", "Open Boards Manager");
|
|
||||||
// if (!action) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// this.boardsListWidgetFrontendContribution.openView({ reveal: true });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private async onUnknownBoard() {
|
|
||||||
// const action = await this.messageService.warn("There's a board connected for which you need to install software." +
|
|
||||||
// " If this were not a PoC we would offer you the right package now.", "Open Boards Manager");
|
|
||||||
// if (!action) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// this.boardsListWidgetFrontendContribution.openView({ reveal: true });
|
|
||||||
// }
|
|
||||||
|
|
||||||
private isArduinoToolbar(maybeToolbarWidget: any): boolean {
|
private isArduinoToolbar(maybeToolbarWidget: any): boolean {
|
||||||
if (maybeToolbarWidget instanceof ArduinoToolbar) {
|
if (maybeToolbarWidget instanceof ArduinoToolbar) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -56,6 +56,7 @@ import { LibraryItemRenderer } from './library/library-item-renderer';
|
|||||||
import { BoardItemRenderer } from './boards/boards-item-renderer';
|
import { BoardItemRenderer } from './boards/boards-item-renderer';
|
||||||
import { MonitorServiceClientImpl } from './monitor/monitor-service-client-impl';
|
import { MonitorServiceClientImpl } from './monitor/monitor-service-client-impl';
|
||||||
import { MonitorServicePath, MonitorService, MonitorServiceClient } from '../common/protocol/monitor-service';
|
import { MonitorServicePath, MonitorService, MonitorServiceClient } from '../common/protocol/monitor-service';
|
||||||
|
import { ConfigService, ConfigServicePath } from '../common/protocol/config-service';
|
||||||
const ElementQueries = require('css-element-queries/src/ElementQueries');
|
const ElementQueries = require('css-element-queries/src/ElementQueries');
|
||||||
|
|
||||||
if (!ARDUINO_PRO_MODE) {
|
if (!ARDUINO_PRO_MODE) {
|
||||||
@ -96,6 +97,9 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
|
|||||||
// Sketch list service
|
// Sketch list service
|
||||||
bind(SketchesService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, SketchesServicePath)).inSingletonScope();
|
bind(SketchesService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, SketchesServicePath)).inSingletonScope();
|
||||||
|
|
||||||
|
// Config service
|
||||||
|
bind(ConfigService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, ConfigServicePath)).inSingletonScope();
|
||||||
|
|
||||||
// Boards service
|
// Boards service
|
||||||
bind(BoardsService).toDynamicValue(context => {
|
bind(BoardsService).toDynamicValue(context => {
|
||||||
const connection = context.container.get(WebSocketConnectionProvider);
|
const connection = context.container.get(WebSocketConnectionProvider);
|
||||||
|
@ -4,6 +4,7 @@ import { WorkspaceServer } from "@theia/workspace/lib/common";
|
|||||||
import { FileSystem, FileStat } from "@theia/filesystem/lib/common";
|
import { FileSystem, FileStat } from "@theia/filesystem/lib/common";
|
||||||
import URI from "@theia/core/lib/common/uri";
|
import URI from "@theia/core/lib/common/uri";
|
||||||
import { SketchFactory } from "./sketch-factory";
|
import { SketchFactory } from "./sketch-factory";
|
||||||
|
import { ConfigService } from "../common/protocol/config-service";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is workaround to have custom frontend binding for the default workspace, although we
|
* This is workaround to have custom frontend binding for the default workspace, although we
|
||||||
@ -21,16 +22,14 @@ export class AWorkspaceService extends WorkspaceService {
|
|||||||
@inject(SketchFactory)
|
@inject(SketchFactory)
|
||||||
protected readonly sketchFactory: SketchFactory;
|
protected readonly sketchFactory: SketchFactory;
|
||||||
|
|
||||||
|
@inject(ConfigService)
|
||||||
|
protected readonly configService: ConfigService;
|
||||||
|
|
||||||
protected async getDefaultWorkspacePath(): Promise<string | undefined> {
|
protected async getDefaultWorkspacePath(): Promise<string | undefined> {
|
||||||
let result = await super.getDefaultWorkspacePath();
|
let result = await super.getDefaultWorkspacePath();
|
||||||
if (!result) {
|
if (!result) {
|
||||||
const userHome = await this.fileSystem.getCurrentUserHome();
|
const config = await this.configService.getConfiguration();
|
||||||
if (!userHome) {
|
result = config.sketchDirUri;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The backend has created this location if it was missing.
|
|
||||||
result = new URI(userHome.uri).resolve('Arduino-PoC').resolve('Sketches').toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const stat = await this.fileSystem.getFileStat(result);
|
const stat = await this.fileSystem.getFileStat(result);
|
||||||
|
@ -38,7 +38,8 @@ export class SketchFactory {
|
|||||||
const sketchDir = parent.resolve(sketchName);
|
const sketchDir = parent.resolve(sketchName);
|
||||||
const sketchFile = sketchDir.resolve(`${sketchName}.ino`);
|
const sketchFile = sketchDir.resolve(`${sketchName}.ino`);
|
||||||
this.fileSystem.createFolder(sketchDir.toString());
|
this.fileSystem.createFolder(sketchDir.toString());
|
||||||
this.fileSystem.createFile(sketchFile.toString(), { content: `
|
this.fileSystem.createFile(sketchFile.toString(), {
|
||||||
|
content: `
|
||||||
void setup() {
|
void setup() {
|
||||||
// put your setup code here, to run once:
|
// put your setup code here, to run once:
|
||||||
|
|
||||||
@ -50,7 +51,11 @@ void loop() {
|
|||||||
}
|
}
|
||||||
` });
|
` });
|
||||||
const location = new URL(window.location.href);
|
const location = new URL(window.location.href);
|
||||||
location.searchParams.set('sketch', sketchFile.toString());
|
location.searchParams.set('sketch', sketchDir.toString());
|
||||||
|
const hash = await this.fileSystem.getFsPath(sketchDir.toString());
|
||||||
|
if (hash) {
|
||||||
|
location.hash = hash;
|
||||||
|
}
|
||||||
this.windowService.openNewWindow(location.toString());
|
this.windowService.openNewWindow(location.toString());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error("Cannot create new sketch: " + e);
|
throw new Error("Cannot create new sketch: " + e);
|
||||||
|
11
arduino-ide-extension/src/common/protocol/config-service.ts
Normal file
11
arduino-ide-extension/src/common/protocol/config-service.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export const ConfigServicePath = '/services/config-service';
|
||||||
|
export const ConfigService = Symbol('ConfigService');
|
||||||
|
|
||||||
|
export interface ConfigService {
|
||||||
|
getConfiguration(): Promise<Config>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Config {
|
||||||
|
sketchDirUri: string;
|
||||||
|
dataDirUri: string;
|
||||||
|
}
|
@ -19,11 +19,24 @@ import { DefaultWorkspaceServerExt } from './default-workspace-server-ext';
|
|||||||
import { WorkspaceServer } from '@theia/workspace/lib/common';
|
import { WorkspaceServer } from '@theia/workspace/lib/common';
|
||||||
import { SketchesServiceImpl } from './sketches-service-impl';
|
import { SketchesServiceImpl } from './sketches-service-impl';
|
||||||
import { SketchesService, SketchesServicePath } from '../common/protocol/sketches-service';
|
import { SketchesService, SketchesServicePath } from '../common/protocol/sketches-service';
|
||||||
|
import { ConfigService, ConfigServicePath } from '../common/protocol/config-service';
|
||||||
import { MonitorServiceImpl } from './monitor/monitor-service-impl';
|
import { MonitorServiceImpl } from './monitor/monitor-service-impl';
|
||||||
import { MonitorService, MonitorServicePath, MonitorServiceClient } from '../common/protocol/monitor-service';
|
import { MonitorService, MonitorServicePath, MonitorServiceClient } from '../common/protocol/monitor-service';
|
||||||
import { MonitorClientProvider } from './monitor/monitor-client-provider';
|
import { MonitorClientProvider } from './monitor/monitor-client-provider';
|
||||||
|
import { ArduinoCli } from './arduino-cli';
|
||||||
|
import { ArduinoCliContribution } from './arduino-cli-contribution';
|
||||||
|
import { CliContribution } from '@theia/core/lib/node';
|
||||||
|
import { ConfigServiceImpl } from './config-service-impl';
|
||||||
|
|
||||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||||
|
// Theia backend CLI contribution.
|
||||||
|
bind(ArduinoCliContribution).toSelf().inSingletonScope();
|
||||||
|
bind(CliContribution).toService(ArduinoCliContribution);
|
||||||
|
|
||||||
|
// Provides the path of the Ardunio CLI.
|
||||||
|
bind(ArduinoCli).toSelf().inSingletonScope();
|
||||||
|
|
||||||
|
// Shared daemonn
|
||||||
bind(ArduinoDaemon).toSelf().inSingletonScope();
|
bind(ArduinoDaemon).toSelf().inSingletonScope();
|
||||||
bind(BackendApplicationContribution).toService(ArduinoDaemon);
|
bind(BackendApplicationContribution).toService(ArduinoDaemon);
|
||||||
|
|
||||||
@ -42,6 +55,15 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
|||||||
bindBackendService(SketchesServicePath, SketchesService);
|
bindBackendService(SketchesServicePath, SketchesService);
|
||||||
});
|
});
|
||||||
bind(ConnectionContainerModule).toConstantValue(sketchesServiceConnectionModule);
|
bind(ConnectionContainerModule).toConstantValue(sketchesServiceConnectionModule);
|
||||||
|
|
||||||
|
bind(ConfigServiceImpl).toSelf().inSingletonScope();
|
||||||
|
bind(ConfigService).toService(ConfigServiceImpl);
|
||||||
|
|
||||||
|
// Config service
|
||||||
|
const configServiceConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => {
|
||||||
|
bindBackendService(ConfigServicePath, ConfigService);
|
||||||
|
});
|
||||||
|
bind(ConnectionContainerModule).toConstantValue(configServiceConnectionModule);
|
||||||
|
|
||||||
// Boards service
|
// Boards service
|
||||||
const boardsServiceConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => {
|
const boardsServiceConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => {
|
||||||
|
27
arduino-ide-extension/src/node/arduino-cli-contribution.ts
Normal file
27
arduino-ide-extension/src/node/arduino-cli-contribution.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { injectable } from 'inversify';
|
||||||
|
import { Argv, Arguments } from 'yargs';
|
||||||
|
import { CliContribution } from '@theia/core/lib/node';
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class ArduinoCliContribution implements CliContribution {
|
||||||
|
|
||||||
|
protected _debugCli = false
|
||||||
|
|
||||||
|
configure(conf: Argv): void {
|
||||||
|
conf.option('debug-cli', {
|
||||||
|
description: 'Can be specified if the CLI daemon process was started externally.',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
nargs: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setArguments(args: Arguments): void {
|
||||||
|
this._debugCli = args['debug-cli'];
|
||||||
|
}
|
||||||
|
|
||||||
|
get debugCli(): boolean {
|
||||||
|
return this._debugCli;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
66
arduino-ide-extension/src/node/arduino-cli.ts
Normal file
66
arduino-ide-extension/src/node/arduino-cli.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import * as os from 'os';
|
||||||
|
import * as which from 'which';
|
||||||
|
import * as cp from 'child_process';
|
||||||
|
import { join, delimiter } from 'path';
|
||||||
|
import { injectable, inject } from 'inversify';
|
||||||
|
import { ILogger } from '@theia/core';
|
||||||
|
import { FileUri } from '@theia/core/lib/node/file-uri';
|
||||||
|
import { Config } from '../common/protocol/config-service';
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class ArduinoCli {
|
||||||
|
|
||||||
|
@inject(ILogger)
|
||||||
|
protected logger: ILogger;
|
||||||
|
|
||||||
|
async getExecPath(): Promise<string> {
|
||||||
|
const build = join(__dirname, '..', '..', 'build');
|
||||||
|
return new Promise<string>((resolve, reject) => {
|
||||||
|
which(`arduino-cli${os.platform() === 'win32' ? '.exe' : ''}`, { path: `${process.env.PATH}${delimiter}${build}` }, (err, path) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve(path);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getDefaultConfig(): Promise<Config> {
|
||||||
|
const command = await this.getExecPath();
|
||||||
|
return new Promise<Config>((resolve, reject) => {
|
||||||
|
cp.execFile(
|
||||||
|
command,
|
||||||
|
['config', 'dump', '--format', 'json'],
|
||||||
|
{ encoding: 'utf8' },
|
||||||
|
(error, stdout, stderr) => {
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stderr) {
|
||||||
|
throw new Error(stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { sketchbook_path, arduino_data } = JSON.parse(stdout.trim());
|
||||||
|
|
||||||
|
if (!sketchbook_path) {
|
||||||
|
reject(new Error(`Could not parse config. 'sketchbook_path' was missing from: ${stdout}`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!arduino_data) {
|
||||||
|
reject(new Error(`Could not parse config. 'arduino_data' was missing from: ${stdout}`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve({
|
||||||
|
sketchDirUri: FileUri.create(sketchbook_path).toString(),
|
||||||
|
dataDirUri: FileUri.create(arduino_data).toString()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,4 @@
|
|||||||
import * as which from 'which';
|
import { join } from 'path';
|
||||||
import * as os from 'os';
|
|
||||||
import { join, delimiter } from 'path';
|
|
||||||
import { exec, spawn, SpawnOptions } from 'child_process';
|
import { exec, spawn, SpawnOptions } from 'child_process';
|
||||||
import { inject, injectable, named } from 'inversify';
|
import { inject, injectable, named } from 'inversify';
|
||||||
import { ILogger } from '@theia/core/lib/common/logger';
|
import { ILogger } from '@theia/core/lib/common/logger';
|
||||||
@ -9,17 +7,22 @@ import { Deferred } from '@theia/core/lib/common/promise-util';
|
|||||||
import { environment } from '@theia/application-package/lib/environment';
|
import { environment } from '@theia/application-package/lib/environment';
|
||||||
import { DaemonLog } from './daemon-log';
|
import { DaemonLog } from './daemon-log';
|
||||||
import { ToolOutputServiceServer } from '../common/protocol/tool-output-service';
|
import { ToolOutputServiceServer } from '../common/protocol/tool-output-service';
|
||||||
|
import { ArduinoCliContribution } from './arduino-cli-contribution';
|
||||||
|
import { ArduinoCli } from './arduino-cli';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class ArduinoDaemon implements BackendApplicationContribution {
|
export class ArduinoDaemon implements BackendApplicationContribution {
|
||||||
|
|
||||||
// Set this to `true` if you want to connect to a CLI running in debug mode.
|
|
||||||
static DEBUG_CLI = false;
|
|
||||||
|
|
||||||
@inject(ILogger)
|
@inject(ILogger)
|
||||||
@named('daemon')
|
@named('daemon')
|
||||||
protected readonly logger: ILogger
|
protected readonly logger: ILogger
|
||||||
|
|
||||||
|
@inject(ArduinoCli)
|
||||||
|
protected readonly cli: ArduinoCli;
|
||||||
|
|
||||||
|
@inject(ArduinoCliContribution)
|
||||||
|
protected readonly cliContribution: ArduinoCliContribution;
|
||||||
|
|
||||||
@inject(ToolOutputServiceServer)
|
@inject(ToolOutputServiceServer)
|
||||||
protected readonly toolOutputService: ToolOutputServiceServer;
|
protected readonly toolOutputService: ToolOutputServiceServer;
|
||||||
|
|
||||||
@ -27,19 +30,10 @@ export class ArduinoDaemon implements BackendApplicationContribution {
|
|||||||
|
|
||||||
async onStart() {
|
async onStart() {
|
||||||
try {
|
try {
|
||||||
if (!ArduinoDaemon.DEBUG_CLI) {
|
if (!this.cliContribution.debugCli) {
|
||||||
const build = join(__dirname, '..', '..', 'build');
|
const executable = await this.cli.getExecPath();
|
||||||
const executable = await new Promise<string>((resolve, reject) => {
|
|
||||||
which(`arduino-cli${os.platform() === 'win32' ? '.exe' : ''}`, { path: `${process.env.PATH}${delimiter}${build}` }, (err, path) => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
resolve(path);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
this.logger.info(`>>> Starting 'arduino-cli' daemon... [${executable}]`);
|
this.logger.info(`>>> Starting 'arduino-cli' daemon... [${executable}]`);
|
||||||
const daemon = exec(`${executable} --debug daemon`, (err, stdout, stderr) => {
|
const daemon = exec(`${executable} daemon -v --log-level info --format json --log-format json`, (err, stdout, stderr) => {
|
||||||
if (err || stderr) {
|
if (err || stderr) {
|
||||||
console.log(err || new Error(stderr));
|
console.log(err || new Error(stderr));
|
||||||
return;
|
return;
|
||||||
@ -74,7 +68,7 @@ export class ArduinoDaemon implements BackendApplicationContribution {
|
|||||||
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||||
this.isReady.resolve();
|
this.isReady.resolve();
|
||||||
if (!ArduinoDaemon.DEBUG_CLI) {
|
if (!this.cliContribution.debugCli) {
|
||||||
this.logger.info(`<<< The 'arduino-cli' daemon is up an running.`);
|
this.logger.info(`<<< The 'arduino-cli' daemon is up an running.`);
|
||||||
} else {
|
} else {
|
||||||
this.logger.info(`Assuming the 'arduino-cli' already runs in debug mode.`);
|
this.logger.info(`Assuming the 'arduino-cli' already runs in debug mode.`);
|
||||||
|
@ -51,6 +51,9 @@ export class CompileReq extends jspb.Message {
|
|||||||
getExportfile(): string;
|
getExportfile(): string;
|
||||||
setExportfile(value: string): void;
|
setExportfile(value: string): void;
|
||||||
|
|
||||||
|
getJobs(): number;
|
||||||
|
setJobs(value: number): void;
|
||||||
|
|
||||||
|
|
||||||
serializeBinary(): Uint8Array;
|
serializeBinary(): Uint8Array;
|
||||||
toObject(includeInstance?: boolean): CompileReq.AsObject;
|
toObject(includeInstance?: boolean): CompileReq.AsObject;
|
||||||
@ -77,6 +80,7 @@ export namespace CompileReq {
|
|||||||
quiet: boolean,
|
quiet: boolean,
|
||||||
vidpid: string,
|
vidpid: string,
|
||||||
exportfile: string,
|
exportfile: string,
|
||||||
|
jobs: number,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +81,8 @@ proto.cc.arduino.cli.commands.CompileReq.toObject = function(includeInstance, ms
|
|||||||
verbose: jspb.Message.getFieldWithDefault(msg, 10, false),
|
verbose: jspb.Message.getFieldWithDefault(msg, 10, false),
|
||||||
quiet: jspb.Message.getFieldWithDefault(msg, 11, false),
|
quiet: jspb.Message.getFieldWithDefault(msg, 11, false),
|
||||||
vidpid: jspb.Message.getFieldWithDefault(msg, 12, ""),
|
vidpid: jspb.Message.getFieldWithDefault(msg, 12, ""),
|
||||||
exportfile: jspb.Message.getFieldWithDefault(msg, 13, "")
|
exportfile: jspb.Message.getFieldWithDefault(msg, 13, ""),
|
||||||
|
jobs: jspb.Message.getFieldWithDefault(msg, 14, 0)
|
||||||
};
|
};
|
||||||
|
|
||||||
if (includeInstance) {
|
if (includeInstance) {
|
||||||
@ -171,6 +172,10 @@ proto.cc.arduino.cli.commands.CompileReq.deserializeBinaryFromReader = function(
|
|||||||
var value = /** @type {string} */ (reader.readString());
|
var value = /** @type {string} */ (reader.readString());
|
||||||
msg.setExportfile(value);
|
msg.setExportfile(value);
|
||||||
break;
|
break;
|
||||||
|
case 14:
|
||||||
|
var value = /** @type {number} */ (reader.readInt32());
|
||||||
|
msg.setJobs(value);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
reader.skipField();
|
reader.skipField();
|
||||||
break;
|
break;
|
||||||
@ -292,6 +297,13 @@ proto.cc.arduino.cli.commands.CompileReq.serializeBinaryToWriter = function(mess
|
|||||||
f
|
f
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
f = message.getJobs();
|
||||||
|
if (f !== 0) {
|
||||||
|
writer.writeInt32(
|
||||||
|
14,
|
||||||
|
f
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -527,6 +539,21 @@ proto.cc.arduino.cli.commands.CompileReq.prototype.setExportfile = function(valu
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* optional int32 jobs = 14;
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
proto.cc.arduino.cli.commands.CompileReq.prototype.getJobs = function() {
|
||||||
|
return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 14, 0));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** @param {number} value */
|
||||||
|
proto.cc.arduino.cli.commands.CompileReq.prototype.setJobs = function(value) {
|
||||||
|
jspb.Message.setProto3IntField(this, 14, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generated by JsPbCodeGenerator.
|
* Generated by JsPbCodeGenerator.
|
||||||
|
14
arduino-ide-extension/src/node/config-service-impl.ts
Normal file
14
arduino-ide-extension/src/node/config-service-impl.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { injectable, inject } from "inversify";
|
||||||
|
import { ConfigService, Config } from "../common/protocol/config-service";
|
||||||
|
import { ArduinoCli } from "./arduino-cli";
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class ConfigServiceImpl implements ConfigService {
|
||||||
|
|
||||||
|
@inject(ArduinoCli)
|
||||||
|
protected readonly cli: ArduinoCli;
|
||||||
|
|
||||||
|
async getConfiguration(): Promise<Config> {
|
||||||
|
return this.cli.getDefaultConfig();
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,12 @@
|
|||||||
import { inject, injectable } from 'inversify';
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
import * as grpc from '@grpc/grpc-js';
|
import * as grpc from '@grpc/grpc-js';
|
||||||
|
import * as PQueue from 'p-queue';
|
||||||
|
import { inject, injectable } from 'inversify';
|
||||||
|
import URI from '@theia/core/lib/common/uri';
|
||||||
|
import { FileSystem } from '@theia/filesystem/lib/common';
|
||||||
|
import { WorkspaceServiceExt } from '../browser/workspace-service-ext';
|
||||||
|
import { ToolOutputServiceServer } from '../common/protocol/tool-output-service';
|
||||||
import { ArduinoCoreClient } from './cli-protocol/commands/commands_grpc_pb';
|
import { ArduinoCoreClient } from './cli-protocol/commands/commands_grpc_pb';
|
||||||
import {
|
import {
|
||||||
InitResp,
|
InitResp,
|
||||||
@ -10,16 +17,10 @@ import {
|
|||||||
UpdateLibrariesIndexReq,
|
UpdateLibrariesIndexReq,
|
||||||
UpdateLibrariesIndexResp
|
UpdateLibrariesIndexResp
|
||||||
} from './cli-protocol/commands/commands_pb';
|
} from './cli-protocol/commands/commands_pb';
|
||||||
import { WorkspaceServiceExt } from '../browser/workspace-service-ext';
|
import { ArduinoCli } from './arduino-cli';
|
||||||
import { FileSystem } from '@theia/filesystem/lib/common';
|
|
||||||
import URI from '@theia/core/lib/common/uri';
|
|
||||||
import { CoreClientProvider, Client } from './core-client-provider';
|
|
||||||
import * as PQueue from 'p-queue';
|
|
||||||
import { ToolOutputServiceServer } from '../common/protocol/tool-output-service';
|
|
||||||
import { Instance } from './cli-protocol/commands/common_pb';
|
import { Instance } from './cli-protocol/commands/common_pb';
|
||||||
import * as fs from 'fs-extra';
|
import { CoreClientProvider, Client } from './core-client-provider';
|
||||||
import * as path from 'path';
|
import { FileUri } from '@theia/core/lib/node';
|
||||||
import * as os from 'os';
|
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class CoreClientProviderImpl implements CoreClientProvider {
|
export class CoreClientProviderImpl implements CoreClientProvider {
|
||||||
@ -36,6 +37,9 @@ export class CoreClientProviderImpl implements CoreClientProvider {
|
|||||||
@inject(ToolOutputServiceServer)
|
@inject(ToolOutputServiceServer)
|
||||||
protected readonly toolOutputService: ToolOutputServiceServer;
|
protected readonly toolOutputService: ToolOutputServiceServer;
|
||||||
|
|
||||||
|
@inject(ArduinoCli)
|
||||||
|
protected readonly cli: ArduinoCli;
|
||||||
|
|
||||||
async getClient(workspaceRootOrResourceUri?: string): Promise<Client | undefined> {
|
async getClient(workspaceRootOrResourceUri?: string): Promise<Client | undefined> {
|
||||||
return this.clientRequestQueue.add(() => new Promise<Client | undefined>(async resolve => {
|
return this.clientRequestQueue.add(() => new Promise<Client | undefined>(async resolve => {
|
||||||
const roots = await this.workspaceServiceExt.roots();
|
const roots = await this.workspaceServiceExt.roots();
|
||||||
@ -76,19 +80,26 @@ export class CoreClientProviderImpl implements CoreClientProvider {
|
|||||||
throw new Error(`Could not resolve filesystem path of URI: ${rootUri}.`);
|
throw new Error(`Could not resolve filesystem path of URI: ${rootUri}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultDownloadsDirPath = path.resolve(os.homedir(), 'Arduino-PoC', 'downloads');
|
const { dataDirUri, sketchDirUri } = await this.cli.getDefaultConfig();
|
||||||
if (!fs.existsSync(defaultDownloadsDirPath)) {
|
const dataDirPath = FileUri.fsPath(dataDirUri);
|
||||||
fs.mkdirpSync(defaultDownloadsDirPath);
|
const sketchDirPath = FileUri.fsPath(sketchDirUri);
|
||||||
|
|
||||||
|
if (!fs.existsSync(dataDirPath)) {
|
||||||
|
fs.mkdirSync(dataDirPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultDataDirPath = path.resolve(os.homedir(), 'Arduino-PoC', 'data')
|
if (!fs.existsSync(sketchDirPath)) {
|
||||||
if (!fs.existsSync(defaultDataDirPath)) {
|
fs.mkdirSync(sketchDirPath);
|
||||||
fs.mkdirpSync(defaultDataDirPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config.setSketchbookdir(rootPath);
|
const downloadDir = path.join(dataDirPath, 'staging');
|
||||||
config.setDatadir(defaultDataDirPath);
|
if (!fs.existsSync(downloadDir)) {
|
||||||
config.setDownloadsdir(defaultDownloadsDirPath);
|
fs.mkdirSync(downloadDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
config.setSketchbookdir(sketchDirPath);
|
||||||
|
config.setDatadir(dataDirPath);
|
||||||
|
config.setDownloadsdir(downloadDir);
|
||||||
config.setBoardmanageradditionalurlsList(['https://downloads.arduino.cc/packages/package_index.json']);
|
config.setBoardmanageradditionalurlsList(['https://downloads.arduino.cc/packages/package_index.json']);
|
||||||
|
|
||||||
const initReq = new InitReq();
|
const initReq = new InitReq();
|
||||||
|
@ -8,7 +8,58 @@ export interface DaemonLog {
|
|||||||
|
|
||||||
export namespace DaemonLog {
|
export namespace DaemonLog {
|
||||||
|
|
||||||
export type Level = 'info' | 'debug' | 'warning' | 'error';
|
export interface Url {
|
||||||
|
readonly Scheme: string;
|
||||||
|
readonly Host: string;
|
||||||
|
readonly Path: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace Url {
|
||||||
|
|
||||||
|
export function is(arg: any | undefined): arg is Url {
|
||||||
|
return !!arg
|
||||||
|
&& typeof arg.Scheme === 'string'
|
||||||
|
&& typeof arg.Host === 'string'
|
||||||
|
&& typeof arg.Path === 'string';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toString(url: Url): string {
|
||||||
|
const { Scheme, Host, Path } = url;
|
||||||
|
return `${Scheme}://${Host}${Path}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface System {
|
||||||
|
readonly os: string;
|
||||||
|
// readonly Resource: Resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace System {
|
||||||
|
export function toString(system: System): string {
|
||||||
|
return `OS: ${system.os}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Tool {
|
||||||
|
readonly version: string;
|
||||||
|
readonly systems: System[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace Tool {
|
||||||
|
|
||||||
|
export function is(arg: any | undefined): arg is Tool {
|
||||||
|
return !!arg && typeof arg.version === 'string' && 'systems' in arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toString(tool: Tool): string {
|
||||||
|
const { version, systems } = tool;
|
||||||
|
return `Version: ${version}${!!systems ? ` Systems: [${tool.systems.map(System.toString).join(', ')}]` : ''}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Level = 'trace' | 'debug' | 'info' | 'warning' | 'error';
|
||||||
|
|
||||||
export function is(arg: any | undefined): arg is DaemonLog {
|
export function is(arg: any | undefined): arg is DaemonLog {
|
||||||
return !!arg
|
return !!arg
|
||||||
@ -20,61 +71,62 @@ export namespace DaemonLog {
|
|||||||
export function toLogLevel(log: DaemonLog): LogLevel {
|
export function toLogLevel(log: DaemonLog): LogLevel {
|
||||||
const { level } = log;
|
const { level } = log;
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case 'info': return LogLevel.INFO;
|
case 'trace': return LogLevel.TRACE;
|
||||||
case 'debug': return LogLevel.DEBUG;
|
case 'debug': return LogLevel.DEBUG;
|
||||||
case 'error': return LogLevel.ERROR;
|
case 'info': return LogLevel.INFO;
|
||||||
case 'warning': return LogLevel.WARN;
|
case 'warning': return LogLevel.WARN;
|
||||||
|
case 'error': return LogLevel.ERROR;
|
||||||
default: return LogLevel.INFO;
|
default: return LogLevel.INFO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function log(logger: ILogger, toLog: string): void {
|
export function log(logger: ILogger, logMessages: string): void {
|
||||||
const segments = toLog.split('time').filter(s => s.trim().length > 0);
|
const parsed = parse(logMessages);
|
||||||
for (const segment of segments) {
|
for (const log of parsed) {
|
||||||
const maybeDaemonLog = parse(`time${segment}`.trim());
|
logger.log(toLogLevel(log), toMessage(log));
|
||||||
for (const logMsg of maybeDaemonLog) {
|
|
||||||
logger.log(toLogLevel(logMsg), logMsg.msg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Super naive.
|
|
||||||
function parse(toLog: string): DaemonLog[] {
|
function parse(toLog: string): DaemonLog[] {
|
||||||
const messages = toLog.split('\ntime=');
|
const messages = toLog.trim().split('\n');
|
||||||
const result: DaemonLog[] = [];
|
const result: DaemonLog[] = [];
|
||||||
for (let i = 0; i < messages.length; i++) {
|
for (let i = 0; i < messages.length; i++) {
|
||||||
const msg = (i > 0 ? 'time=' : '') + messages[i];
|
try {
|
||||||
const rawSegments = msg.split(/(\s+)/)
|
const maybeDaemonLog = JSON.parse(messages[i]);
|
||||||
.map(segment => segment.replace(/['"]+/g, ''))
|
if (DaemonLog.is(maybeDaemonLog)) {
|
||||||
.map(segment => segment.trim())
|
result.push(maybeDaemonLog);
|
||||||
.filter(segment => segment.length > 0);
|
continue;
|
||||||
|
}
|
||||||
const timeIndex = rawSegments.findIndex(segment => segment.startsWith('time='));
|
} catch { /* NOOP */ }
|
||||||
const levelIndex = rawSegments.findIndex(segment => segment.startsWith('level='));
|
|
||||||
const msgIndex = rawSegments.findIndex(segment => segment.startsWith('msg='));
|
|
||||||
if (rawSegments.length > 2
|
|
||||||
&& timeIndex !== -1
|
|
||||||
&& levelIndex !== -1
|
|
||||||
&& msgIndex !== -1) {
|
|
||||||
result.push({
|
|
||||||
time: rawSegments[timeIndex].split('=')[1],
|
|
||||||
level: rawSegments[levelIndex].split('=')[1] as Level,
|
|
||||||
msg: [rawSegments[msgIndex].split('=')[1], ...rawSegments.slice(msgIndex + 1)].join(' ')
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
result.push({
|
result.push({
|
||||||
time: new Date().toString(),
|
time: new Date().toString(),
|
||||||
level: 'info',
|
level: 'info',
|
||||||
msg: msg
|
msg: messages[i]
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Otherwise, log the string as is.
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toPrettyString(logMessage: string): string {
|
export function toPrettyString(logMessages: string): string {
|
||||||
const parsed = parse(logMessage);
|
const parsed = parse(logMessages);
|
||||||
return parsed.map(msg => `[${msg.level.toUpperCase() || 'INFO'}] ${msg.msg}\n`).join('');
|
return parsed.map(toMessage).join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toMessage(log: DaemonLog): string {
|
||||||
|
const details = Object.keys(log).filter(key => key !== 'msg' && key !== 'level' && key !== 'time').map(key => toDetails(log, key)).join(', ');
|
||||||
|
return `[${log.level.toUpperCase()}] ${log.msg}${!!details ? ` [${details}]` : ''}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function toDetails(log: DaemonLog, key: string): string {
|
||||||
|
let value = (log as any)[key];
|
||||||
|
if (DaemonLog.Url.is(value)) {
|
||||||
|
value = DaemonLog.Url.toString(value);
|
||||||
|
} else if (DaemonLog.Tool.is(value)) {
|
||||||
|
value = DaemonLog.Tool.toString(value);
|
||||||
|
} else if (typeof value === 'object') {
|
||||||
|
value = JSON.stringify(value).replace(/\"([^(\")"]+)\":/g, '$1:'); // Remove the quotes from the property keys.
|
||||||
|
}
|
||||||
|
return `${key.toLowerCase()}: ${value}`;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,14 +1,15 @@
|
|||||||
import * as os from 'os';
|
import { injectable, inject } from 'inversify';
|
||||||
import * as path from 'path';
|
|
||||||
import { injectable } from 'inversify';
|
|
||||||
import { FileUri } from '@theia/core/lib/node/file-uri';
|
|
||||||
import { DefaultWorkspaceServer } from '@theia/workspace/lib/node/default-workspace-server';
|
import { DefaultWorkspaceServer } from '@theia/workspace/lib/node/default-workspace-server';
|
||||||
|
import { ConfigService } from '../common/protocol/config-service';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class DefaultWorkspaceServerExt extends DefaultWorkspaceServer {
|
export class DefaultWorkspaceServerExt extends DefaultWorkspaceServer {
|
||||||
|
|
||||||
|
@inject(ConfigService) protected readonly configService: ConfigService;
|
||||||
|
|
||||||
protected async getWorkspaceURIFromCli(): Promise<string | undefined> {
|
protected async getWorkspaceURIFromCli(): Promise<string | undefined> {
|
||||||
return FileUri.create(path.join(os.homedir(), 'Arduino-PoC', 'Sketches')).toString();
|
const config = await this.configService.getConfiguration();
|
||||||
|
return config.sketchDirUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"name": "arduino-ide-browser",
|
"name": "browser-app",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -30,7 +30,7 @@
|
|||||||
"theia": {
|
"theia": {
|
||||||
"frontend": {
|
"frontend": {
|
||||||
"config": {
|
"config": {
|
||||||
"applicationName": "Arduino-PoC",
|
"applicationName": "Arduino Editor",
|
||||||
"defaultTheme": "arduino-theme",
|
"defaultTheme": "arduino-theme",
|
||||||
"preferences": {
|
"preferences": {
|
||||||
"editor.autoSave": "on"
|
"editor.autoSave": "on"
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"name": "arduino-ide-electron",
|
"name": "electron-app",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -33,7 +33,7 @@
|
|||||||
"target": "electron",
|
"target": "electron",
|
||||||
"frontend": {
|
"frontend": {
|
||||||
"config": {
|
"config": {
|
||||||
"applicationName": "Arduino-PoC",
|
"applicationName": "Arduino Editor",
|
||||||
"defaultTheme": "arduino-theme",
|
"defaultTheme": "arduino-theme",
|
||||||
"preferences": {
|
"preferences": {
|
||||||
"editor.autoSave": "on"
|
"editor.autoSave": "on"
|
@ -14,7 +14,7 @@
|
|||||||
const workingCopy = 'working-copy';
|
const workingCopy = 'working-copy';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Relative path from the `__dirname` to the root where the `arduino-ide-extension` and the `arduino-ide-electron` folders are.
|
* Relative path from the `__dirname` to the root where the `arduino-ide-extension` and the `electron-app` folders are.
|
||||||
* This could come handy when moving the location of the `electron/packager`.
|
* This could come handy when moving the location of the `electron/packager`.
|
||||||
*/
|
*/
|
||||||
const rootPath = join('..', '..');
|
const rootPath = join('..', '..');
|
||||||
@ -42,18 +42,18 @@
|
|||||||
// Copy the following items into the `working-copy` folder. Make sure to reuse the `yarn.lock`. |
|
// Copy the following items into the `working-copy` folder. Make sure to reuse the `yarn.lock`. |
|
||||||
//----------------------------------------------------------------------------------------------+
|
//----------------------------------------------------------------------------------------------+
|
||||||
mkdir('-p', path('..', workingCopy));
|
mkdir('-p', path('..', workingCopy));
|
||||||
for (const name of ['arduino-ide-extension', 'arduino-ide-electron', 'yarn.lock', 'package.json', 'lerna.json']) {
|
for (const name of ['arduino-ide-extension', 'electron-app', 'yarn.lock', 'package.json', 'lerna.json']) {
|
||||||
cp('-rf', path(rootPath, name), path('..', workingCopy));
|
cp('-rf', path(rootPath, name), path('..', workingCopy));
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------+
|
//-----------------------------------------------------+
|
||||||
// No need to build the `arduino-ide-browser` example. |
|
// No need to build the `browser-app` example. |
|
||||||
//-----------------------------------------------------+
|
//-----------------------------------------------------+
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
let pkg = require('../working-copy/package.json');
|
let pkg = require('../working-copy/package.json');
|
||||||
const workspaces = pkg.workspaces;
|
const workspaces = pkg.workspaces;
|
||||||
// We cannot remove the `arduino-ide-electron`. Otherwise, there is not way to collect the unused dependencies.
|
// We cannot remove the `electron-app`. Otherwise, there is not way to collect the unused dependencies.
|
||||||
const dependenciesToRemove = ['arduino-ide-browser'];
|
const dependenciesToRemove = ['browser-app'];
|
||||||
for (const dependencyToRemove of dependenciesToRemove) {
|
for (const dependencyToRemove of dependenciesToRemove) {
|
||||||
const index = workspaces.indexOf(dependencyToRemove);
|
const index = workspaces.indexOf(dependencyToRemove);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
@ -70,13 +70,13 @@
|
|||||||
// Collect all unused dependencies by the backend. We have to remove them from the electron app.
|
// 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.
|
// The `bundle.js` already contains everything we need for the frontend.
|
||||||
// We have to do it before changing the dependencies to `local-path`.
|
// We have to do it before changing the dependencies to `local-path`.
|
||||||
const unusedDependencies = await utils.collectUnusedDependencies('../working-copy/arduino-ide-electron/');
|
const unusedDependencies = await utils.collectUnusedDependencies('../working-copy/electron-app/');
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------+
|
//------------------------------------------------------------------------------------+
|
||||||
// Merge the `working-copy/package.json` with `electron/build/template-package.json`. |
|
// Merge the `working-copy/package.json` with `electron/build/template-package.json`. |
|
||||||
//------------------------------------------------------------------------------------+
|
//------------------------------------------------------------------------------------+
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
pkg = require('../working-copy/arduino-ide-electron/package.json');
|
pkg = require('../working-copy/electron-app/package.json');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const template = require('../build/template-package.json');
|
const template = require('../build/template-package.json');
|
||||||
template.build.files = [ ...template.build.files, ...unusedDependencies.map(name => `!node_modules/${name}`) ];
|
template.build.files = [ ...template.build.files, ...unusedDependencies.map(name => `!node_modules/${name}`) ];
|
||||||
|
12
package.json
12
package.json
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "arduino-poc",
|
"name": "arduino-editor",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"description": "A PoC demonstrating an Arduino IDE built using Eclipse Theia",
|
"description": "Arduino IDE built using Eclipse Theia",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": "https://github.com/bcmi-labs/arduino-editor.git",
|
"repository": "https://github.com/bcmi-labs/arduino-editor.git",
|
||||||
"author": "Christian Weichel <christian.weichel@typefox.io>",
|
"author": "Christian Weichel <christian.weichel@typefox.io>",
|
||||||
@ -14,12 +14,12 @@
|
|||||||
"prepare": "lerna run prepare",
|
"prepare": "lerna run prepare",
|
||||||
"rebuild:browser": "theia rebuild:browser",
|
"rebuild:browser": "theia rebuild:browser",
|
||||||
"rebuild:electron": "theia rebuild:electron",
|
"rebuild:electron": "theia rebuild:electron",
|
||||||
"start": "cd arduino-ide-browser && yarn start",
|
"start": "yarn --cwd ./browser-app start",
|
||||||
"watch": "lerna run watch --parallel"
|
"watch": "lerna run watch --parallel"
|
||||||
},
|
},
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"arduino-ide-electron",
|
"arduino-ide-extension",
|
||||||
"arduino-ide-browser",
|
"electron-app",
|
||||||
"arduino-ide-extension"
|
"browser-app"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -4473,7 +4473,7 @@ decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1:
|
|||||||
is-stream "^1.1.0"
|
is-stream "^1.1.0"
|
||||||
tar-stream "^1.5.2"
|
tar-stream "^1.5.2"
|
||||||
|
|
||||||
decompress-tarbz2@^4.0.0, decompress-tarbz2@^4.1.1:
|
decompress-tarbz2@^4.0.0:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b"
|
resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b"
|
||||||
integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==
|
integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==
|
||||||
@ -4484,7 +4484,7 @@ decompress-tarbz2@^4.0.0, decompress-tarbz2@^4.1.1:
|
|||||||
seek-bzip "^1.0.5"
|
seek-bzip "^1.0.5"
|
||||||
unbzip2-stream "^1.0.9"
|
unbzip2-stream "^1.0.9"
|
||||||
|
|
||||||
decompress-targz@^4.0.0:
|
decompress-targz@^4.0.0, decompress-targz@^4.1.1:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee"
|
resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee"
|
||||||
integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==
|
integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==
|
||||||
@ -8098,7 +8098,7 @@ modify-values@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"
|
resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"
|
||||||
integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==
|
integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==
|
||||||
|
|
||||||
moment@^2.10.6, moment@^2.18.1, moment@^2.21.0:
|
moment@^2.10.6, moment@^2.18.1, moment@^2.21.0, moment@^2.24.0:
|
||||||
version "2.24.0"
|
version "2.24.0"
|
||||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
|
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
|
||||||
integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
|
integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
|
||||||
|
Loading…
x
Reference in New Issue
Block a user