Moving repo to bmci-labs

This commit is contained in:
Christian Weichel 2019-05-06 10:25:29 +02:00
commit 201351fea8
61 changed files with 16427 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
node_modules/
# .node_modules is a hack for the electron builder.
.node_modules/
lib/
build/
!electron/build/
src-gen/
arduino-ide-*/webpack.config.js
.DS_Store

20
.gitpod.yml Normal file
View File

@ -0,0 +1,20 @@
image:
file: Dockerfile
ports:
- port: 3000
onOpen: open-browser
tasks:
- init: >
yarn &&
yarn --cwd ./arduino-ide-browser start
github:
prebuilds:
master: true
branches: true
pullRequests: true
pullRequestsFromForks: true
addComment: false
addBadge: false

32
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,32 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Backend",
"program": "${workspaceRoot}/arduino-ide-browser/src-gen/backend/main.js",
"args": [
"--hostname=0.0.0.0",
"--port=3000",
"--no-cluster",
"--no-app-auto-install"
],
"env": {
"NODE_ENV": "development"
},
"sourceMaps": true,
"outFiles": [
"${workspaceRoot}/arduino-ide-browser/src-gen/backend/*.js",
"${workspaceRoot}/arduino-ide-browser/lib/**/*.js",
"${workspaceRoot}/arduino-ide-extension/*/lib/**/*.js"
],
"smartStep": true,
"internalConsoleOptions": "openOnSessionStart",
"outputCapture": "std"
}
]
}

18
Dockerfile Normal file
View File

@ -0,0 +1,18 @@
FROM gitpod/workspace-full
USER root
RUN apt-get update -q --fix-missing && \
apt-get install -y -q software-properties-common && \
apt-get install -y -q --no-install-recommends \
build-essential \
libssl-dev \
golang-go \
libxkbfile-dev
RUN set -ex && \
tmpdir=$(mktemp -d) && \
curl -L -o $tmpdir/protoc.zip https://github.com/protocolbuffers/protobuf/releases/download/v3.6.1/protoc-3.6.1-linux-x86_64.zip && \
mkdir -p /usr/lib/protoc && cd /usr/lib/protoc && unzip $tmpdir/protoc.zip && \
chmod -R 755 /usr/lib/protoc/include/google && \
ln -s /usr/lib/protoc/bin/* /usr/bin && \
rm $tmpdir/protoc.zip

19
README.md Normal file
View File

@ -0,0 +1,19 @@
# Arduino IDE PoC
> **Beware:** This is very much work-in-progress. Things can and probably will be broken, even on master.
This repo contains a proof-of-concept for an Arduino IDE based on Theia.
It's built on top of a [version of the arduino-cli](https://github.com/cmaglie/arduino-cli/tree/daemon) that sports a gRPC interface.
## How to try (online)
The easiest way to try the browser version is using Gitpod: https://gitpod.io/#github.com/typefox/arduino-poc
## How to try (offline)
requires [protoc](https://github.com/protocolbuffers/protobuf/releases/tag/v3.7.1) to be in the `PATH` and some other [prerequisites](https://github.com/theia-ide/theia/blob/master/doc/Developing.md#prerequisites).
```
git clone https://github.com/typefox/arduino-poc
cd arduino-poc
yarn
yarn --cwd arduino-ide-electron start
```

View File

@ -0,0 +1,30 @@
{
"private": true,
"name": "arduino-ide-browser",
"version": "0.0.1",
"license": "MIT",
"dependencies": {
"@theia/core": "next",
"@theia/editor": "next",
"@theia/file-search": "next",
"@theia/filesystem": "next",
"@theia/languages": "next",
"@theia/messages": "next",
"@theia/monaco": "next",
"@theia/navigator": "next",
"@theia/preferences": "next",
"@theia/process": "next",
"@theia/terminal": "next",
"@theia/workspace": "next",
"@theia/textmate-grammars": "next",
"arduino-ide-extension": "0.0.1"
},
"devDependencies": {
"@theia/cli": "next"
},
"scripts": {
"prepare": "theia build --mode development",
"start": "theia start --root-dir=../workspace",
"watch": "theia build --watch --mode development"
}
}

View File

@ -0,0 +1,39 @@
{
"private": true,
"name": "arduino-ide-electron",
"version": "0.0.1",
"license": "MIT",
"dependencies": {
"@theia/core": "next",
"@theia/editor": "next",
"@theia/electron": "next",
"@theia/file-search": "next",
"@theia/filesystem": "next",
"@theia/languages": "next",
"@theia/messages": "next",
"@theia/monaco": "next",
"@theia/navigator": "next",
"@theia/preferences": "next",
"@theia/process": "next",
"@theia/terminal": "next",
"@theia/workspace": "next",
"@theia/textmate-grammars": "next",
"arduino-ide-extension": "0.0.1"
},
"devDependencies": {
"@theia/cli": "next"
},
"scripts": {
"prepare": "theia build --mode development",
"start": "theia start --root-dir=../workspace",
"watch": "theia build --watch --mode development"
},
"theia": {
"target": "electron",
"backend": {
"config": {
"startupTimeout": -1
}
}
}
}

1
arduino-ide-extension/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
src/node/cli-protocol

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,42 @@
{
"name": "arduino-ide-extension",
"version": "0.0.1",
"description": "An extension for Theia building the Arduino IDE",
"license": "MIT",
"dependencies": {
"@theia/core": "next",
"@theia/editor": "next",
"@theia/filesystem": "next",
"@theia/languages": "next",
"@theia/monaco": "next",
"@theia/workspace": "next"
},
"scripts": {
"generate-protoc": "./scripts/generate-protoc.sh",
"prepare": "yarn run clean && yarn generate-protoc && yarn run build",
"clean": "rimraf lib",
"lint": "tslint -c ./tslint.json --project ./tsconfig.json",
"build": "tsc && cp -rf src/node/cli-protocol lib/node && yarn lint",
"watch": "tsc -w"
},
"devDependencies": {
"@types/google-protobuf": "^3.2.7",
"grpc-tools": "^1.7.3",
"grpc_tools_node_protoc_ts": "^2.5.0",
"rimraf": "^2.6.1",
"tslint": "^5.5.0",
"typescript": "2.9.1"
},
"files": [
"lib",
"src",
"build",
"data"
],
"theiaExtensions": [
{
"backend": "lib/node/arduino-backend-module",
"frontend": "lib/browser/arduino-frontend-module"
}
]
}

View File

@ -0,0 +1,45 @@
#!/bin/bash
SCRIPT=`realpath -s $0`
SCRIPTPATH=`dirname $SCRIPT`
WORKDIR=/tmp/arduino-cli-protoc
echo "Working in $WORKDIR"
# this could be a Git submodule, but that feels to clunky for just building the protobuf stuff
mkdir -p $WORKDIR
pushd $WORKDIR
if [ ! -d arduino-cli ]; then
git clone https://github.com/cmaglie/arduino-cli
cd arduino-cli
git checkout daemon
cd -
mkdir -p go/src/github.com/arduino
ln -s $PWD/arduino-cli go/src/github.com/arduino
export GOPATH=$PWD/go
cd go/src/github.com/arduino/arduino-cli
GOOS=linux go build -o arduino-cli.linux
# GOOS=darwin go build -o arduino-cli.darwin
fi
popd
# make sure the output path exists
mkdir -p src/node/cli-protocol
export PATH=$PATH:$PWD/node_modules/.bin
# generate js codes via grpc-tools
grpc_tools_node_protoc \
--js_out=import_style=commonjs,binary:./src/node/cli-protocol \
--grpc_out=./src/node/cli-protocol \
--plugin=protoc-gen-grpc=`which grpc_tools_node_protoc_plugin` \
-I /usr/lib/protoc/include \
-I $WORKDIR/arduino-cli/rpc \
$WORKDIR/arduino-cli/rpc/*.proto
# generate d.ts codes
protoc \
--plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts \
--ts_out=./src/node/cli-protocol \
-I /usr/lib/protoc/include \
-I $WORKDIR/arduino-cli/rpc \
$WORKDIR/arduino-cli/rpc/*.proto

View File

@ -0,0 +1,13 @@
import { Command } from '@theia/core/lib/common/command';
export namespace ArduinoCommands {
export const VERIFY: Command = {
id: 'arduino-verify'
}
export const UPLOAD: Command = {
id: 'arduino-upload'
}
}

View File

@ -0,0 +1,103 @@
import * as React from 'react';
import { injectable, inject, postConstruct } from 'inversify';
import URI from '@theia/core/lib/common/uri';
import { EditorWidget } from '@theia/editor/lib/browser/editor-widget';
import { MessageService } from '@theia/core/lib/common/message-service';
import { CommandContribution, CommandRegistry } from '@theia/core/lib/common/command';
import { DefaultFrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
import { BoardsService } from '../common/protocol/boards-service';
import { ArduinoCommands } from './arduino-commands';
import { ConnectedBoards } from './components/connected-boards';
import { CoreService } from '../common/protocol/core-service';
import { WorkspaceServiceExt } from './workspace-service-ext';
@injectable()
export class ArduinoFrontendContribution extends DefaultFrontendApplicationContribution implements TabBarToolbarContribution, CommandContribution {
@inject(MessageService)
protected readonly messageService: MessageService;
@inject(BoardsService)
protected readonly boardService: BoardsService;
@inject(CoreService)
protected readonly coreService: CoreService;
@inject(WorkspaceServiceExt)
protected readonly workspaceServiceExt: WorkspaceServiceExt;
@postConstruct()
protected async init(): Promise<void> {
// This is a hack. Otherwise, the backend services won't bind.
await this.workspaceServiceExt.roots();
}
registerToolbarItems(registry: TabBarToolbarRegistry): void {
registry.registerItem({
id: ArduinoCommands.VERIFY.id,
command: ArduinoCommands.VERIFY.id,
tooltip: 'Verify',
group: 'arduino',
text: '$(check)'
});
registry.registerItem({
id: ArduinoCommands.UPLOAD.id,
command: ArduinoCommands.UPLOAD.id,
tooltip: 'Upload',
group: 'arduino',
text: '$(arrow-right)'
});
registry.registerItem({
id: ConnectedBoards.TOOLBAR_ID,
render: () => <ConnectedBoards boardsService={this.boardService}/>,
isVisible: widget => this.isArduinoEditor(widget)
})
}
registerCommands(registry: CommandRegistry): void {
registry.registerCommand(ArduinoCommands.VERIFY, {
isVisible: widget => this.isArduinoEditor(widget),
isEnabled: widget => this.isArduinoEditor(widget),
execute: async widget => {
const uri = this.toUri(widget);
if (uri) {
const result = await this.coreService.compile({ uri: uri.toString() });
console.log('compile result', result);
}
}
});
registry.registerCommand(ArduinoCommands.UPLOAD, {
isVisible: widget => this.isArduinoEditor(widget),
isEnabled: widget => this.isArduinoEditor(widget),
execute: widget => {
const uri = this.toUri(widget);
if (uri) {
this.messageService.info(`Uploading ${uri.toString()}`);
}
}
});
}
private isArduinoEditor(maybeEditorWidget: any): boolean {
if (maybeEditorWidget instanceof EditorWidget) {
return maybeEditorWidget.editor.uri.toString().endsWith('.ino');
}
return false;
}
private toUri(arg: any): URI | undefined {
if (arg instanceof URI) {
return arg;
}
if (typeof arg === 'string') {
return new URI(arg);
}
if (arg instanceof EditorWidget) {
return arg.editor.uri;
}
return undefined;
}
}

View File

@ -0,0 +1,70 @@
import { ContainerModule, interfaces } from '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 } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
import { WebSocketConnectionProvider } from '@theia/core/lib/browser/messaging/ws-connection-provider';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application'
import { LanguageGrammarDefinitionContribution } from '@theia/monaco/lib/browser/textmate';
import { LibraryListWidget } from './library/library-list-widget';
import { ArduinoFrontendContribution } from './arduino-frontend-contribution';
import { ArduinoLanguageGrammarContribution } from './language/arduino-language-grammar-contribution';
import { LibraryService, LibraryServicePath } from '../common/protocol/library-service';
import { BoardsService, BoardsServicePath } from '../common/protocol/boards-service';
import { LibraryListWidgetFrontendContribution } from './library/list-widget-frontend-contribution';
import { CoreService, CoreServicePath } from '../common/protocol/core-service';
import { BoardsListWidget } from './boards/boards-list-widget';
import { BoardsListWidgetFrontendContribution } from './boards/boards-widget-frontend-contribution';
import { WorkspaceServiceExt, WorkspaceServiceExtPath } from './workspace-service-ext';
import { WorkspaceServiceExtImpl } from './workspace-service-ext-impl';
import '../../src/browser/style/index.css';
export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Unbind, isBound: interfaces.IsBound, rebind: interfaces.Rebind) => {
// Commands and toolbar items
bind(ArduinoFrontendContribution).toSelf().inSingletonScope();
bind(CommandContribution).toService(ArduinoFrontendContribution);
bind(TabBarToolbarContribution).toService(ArduinoFrontendContribution);
// `ino` TextMate grammar
bind(LanguageGrammarDefinitionContribution).to(ArduinoLanguageGrammarContribution).inSingletonScope();
// Library service
bind(LibraryService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, LibraryServicePath)).inSingletonScope();
// Library list widget
bind(LibraryListWidget).toSelf();
bindViewContribution(bind, LibraryListWidgetFrontendContribution);
bind(WidgetFactory).toDynamicValue(context => ({
id: LibraryListWidget.WIDGET_ID,
createWidget: () => context.container.get(LibraryListWidget)
}));
bind(FrontendApplicationContribution).toService(LibraryListWidgetFrontendContribution);
// Boards service
bind(BoardsService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, BoardsServicePath)).inSingletonScope();
// Boards list widget
bind(BoardsListWidget).toSelf();
bindViewContribution(bind, BoardsListWidgetFrontendContribution);
bind(WidgetFactory).toDynamicValue(context => ({
id: BoardsListWidget.WIDGET_ID,
createWidget: () => context.container.get(BoardsListWidget)
}));
bind(FrontendApplicationContribution).toService(BoardsListWidgetFrontendContribution);
// Core service
bind(CoreService)
.toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, CoreServicePath))
.inSingletonScope();
// The workspace service extension
bind(WorkspaceServiceExt).to(WorkspaceServiceExtImpl).inSingletonScope().onActivation(({ container }, workspaceServiceExt) => {
WebSocketConnectionProvider.createProxy(container, WorkspaceServiceExtPath, workspaceServiceExt);
// Eagerly active the core, library, and boards services.
container.get(CoreService);
container.get(LibraryService);
container.get(BoardsService);
return workspaceServiceExt;
});
});

View File

@ -0,0 +1,16 @@
import { ListWidget } from './list-widget';
export class BoardsListWidget extends ListWidget {
static WIDGET_ID = 'boards-list-widget';
static WIDGET_LABEL = 'Boards Manager';
protected widgetProps(): ListWidget.Props {
return {
id: BoardsListWidget.WIDGET_ID,
title: BoardsListWidget.WIDGET_LABEL,
iconClass: 'fa fa-microchip'
}
}
}

View File

@ -0,0 +1,32 @@
import { injectable } from 'inversify';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
import { ListWidget } from './list-widget';
import { BoardsListWidget } from './boards-list-widget';
@injectable()
export abstract class ListWidgetFrontendContribution extends AbstractViewContribution<ListWidget> implements FrontendApplicationContribution {
async initializeLayout(): Promise<void> {
await this.openView();
}
}
@injectable()
export class BoardsListWidgetFrontendContribution extends ListWidgetFrontendContribution {
constructor() {
super({
widgetId: BoardsListWidget.WIDGET_ID,
widgetName: BoardsListWidget.WIDGET_LABEL,
defaultWidgetOptions: {
area: 'left',
rank: 600
},
toggleCommandId: `${BoardsListWidget.WIDGET_ID}:toggle`,
toggleKeybinding: 'ctrlcmd+shift+b'
});
}
}

View File

@ -0,0 +1,72 @@
import * as React from 'react';
import { inject, injectable, postConstruct } from 'inversify';
import { Message } from '@phosphor/messaging';
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
import { WindowService } from '@theia/core/lib/browser/window/window-service';
import { FilterableListContainer } from '../components/component-list/filterable-list-container';
import { BoardsService } from '../../common/protocol/boards-service';
@injectable()
export abstract class ListWidget extends ReactWidget {
@inject(BoardsService)
protected readonly boardsService: BoardsService;
@inject(WindowService)
protected readonly windowService: WindowService;
constructor() {
super();
const { id, title, iconClass } = this.widgetProps();
this.id = id;
this.title.label = title;
this.title.caption = title;
this.title.iconClass = iconClass;
this.title.closable = true;
this.addClass(ListWidget.Styles.LIST_WIDGET_CLASS);
this.node.tabIndex = 0; // To be able to set the focus on the widget.
}
protected abstract widgetProps(): ListWidget.Props;
@postConstruct()
protected init(): void {
this.update();
}
protected onActivateRequest(msg: Message): void {
super.onActivateRequest(msg);
this.node.focus();
this.render();
}
protected onUpdateRequest(msg: Message): void {
super.onUpdateRequest(msg);
this.render();
}
render(): React.ReactNode {
return <FilterableListContainer
service={this.boardsService}
windowService={this.windowService}
/>;
}
}
export namespace ListWidget {
/**
* Props for customizing the abstract list widget.
*/
export interface Props {
readonly id: string;
readonly title: string;
readonly iconClass: string;
}
export namespace Styles {
export const LIST_WIDGET_CLASS = 'arduino-list-widget'
}
}

View File

@ -0,0 +1,73 @@
import * as React from 'react';
import { WindowService } from '@theia/core/lib/browser/window/window-service';
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
export class ComponentListItem extends React.Component<ComponentListItem.Props> {
private onClick = (event: React.SyntheticEvent<HTMLAnchorElement, Event>) => {
const { target } = event.nativeEvent;
if (target instanceof HTMLAnchorElement) {
this.props.windowService.openNewWindow(target.href);
event.nativeEvent.preventDefault();
}
}
render(): React.ReactNode {
const { item } = this.props;
const style = ComponentListItem.Styles;
const name = <span className={style.NAME_CLASS}>{item.name}</span>;
const author = <span className={style.AUTHOR_CLASS}>{item.author}</span>;
const installedVersion = !!item.installedVersion && <React.Fragment>
<span className={style.VERSION_CLASS}>Version {item.installedVersion}</span>
<span className={style.INSTALLED_CLASS}>INSTALLED</span>
</React.Fragment>;
const summary = <div className={style.SUMMARY_CLASS}>{item.summary}</div>;
const description = !!item.description && <div className={style.DESCRIPTION_CLASS}>{item.description}</div>;
const moreInfo = !!item.moreInfoLink && <a href={item.moreInfoLink} onClick={this.onClick}>More info</a>;
const install = item.installable && !item.installedVersion && <button className={style.INSTALL_BTN_CLASS}>INSTALL</button>;
return <div className={[style.LIST_ITEM_CLASS, style.NO_SELECT_CLASS].join(' ')}>
<div className={style.HEADER_CLASS}>
<span>{name} by {author}</span>
{installedVersion}
</div>
<div className={style.CONTENT_CLASS}>
{summary}
{description}
</div>
<div className={style.FOOTER_CLASS}>
{moreInfo}
{install}
</div>
</div>;
}
}
export namespace ComponentListItem {
export interface Props {
readonly item: ArduinoComponent;
readonly windowService: WindowService;
}
export namespace Styles {
export const LIST_ITEM_CLASS = 'component-list-item';
export const HEADER_CLASS = 'header';
export const CONTENT_CLASS = 'content';
export const FOOTER_CLASS = 'footer';
export const INSTALLED_CLASS = 'installed';
export const NO_SELECT_CLASS = 'noselect';
export const NAME_CLASS = 'name';
export const AUTHOR_CLASS = 'author';
export const VERSION_CLASS = 'version';
export const SUMMARY_CLASS = 'summary';
export const DESCRIPTION_CLASS = 'description';
export const INSTALL_BTN_CLASS = 'install';
}
}

View File

@ -0,0 +1,23 @@
import * as React from 'react';
import { WindowService } from '@theia/core/lib/browser/window/window-service';
import { ComponentListItem } from './component-list-item';
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
export class ComponentList extends React.Component<ComponentList.Props> {
render(): React.ReactNode {
return <div>
{this.props.items.map(item => <ComponentListItem key={item.name} item={item} windowService={this.props.windowService}/>)}
</div>;
}
}
export namespace ComponentList {
export interface Props {
readonly items: ArduinoComponent[];
readonly windowService: WindowService;
}
}

View File

@ -0,0 +1,68 @@
import * as React from 'react';
import { WindowService } from '@theia/core/lib/browser/window/window-service';
import { ComponentList } from './component-list';
import { SearchBar } from './search-bar';
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
export class FilterableListContainer extends React.Component<FilterableListContainer.Props, FilterableListContainer.State> {
constructor(props: Readonly<FilterableListContainer.Props>) {
super(props);
this.state = {
filterText: '',
items: []
};
this.handleFilterTextChange = this.handleFilterTextChange.bind(this);
}
componentWillMount(): void {
this.handleFilterTextChange('');
}
render(): React.ReactNode {
return <div className={FilterableListContainer.Styles.FILTERABLE_LIST_CONTAINER_CLASS}>
<SearchBar
filterText={this.state.filterText}
onFilterTextChanged={this.handleFilterTextChange}
/>
<ComponentList
items={this.state.items}
windowService={this.props.windowService}
/>
</div>
}
private handleFilterTextChange(filterText: string): void {
this.props.service.search({ query: filterText }).then(result => {
const { items } = result;
this.setState({
filterText,
items
});
});
}
}
export namespace FilterableListContainer {
export interface Props {
readonly service: ComponentSource;
readonly windowService: WindowService;
}
export interface State {
filterText: string;
items: ArduinoComponent[];
}
export namespace Styles {
export const FILTERABLE_LIST_CONTAINER_CLASS = 'filterable-list-container';
}
export interface ComponentSource {
search(req: { query: string }): Promise<{ items: ArduinoComponent[] }>
}
}

View File

@ -0,0 +1,39 @@
import * as React from 'react';
export class SearchBar extends React.Component<SearchBar.Props> {
constructor(props: Readonly<SearchBar.Props>) {
super(props);
this.handleFilterTextChange = this.handleFilterTextChange.bind(this);
}
render(): React.ReactNode {
return <form className={SearchBar.Styles.SEARCH_BAR_CLASS}>
<input
type='text'
placeholder='Search'
size={1}
value={this.props.filterText}
onChange={this.handleFilterTextChange}
/>
</form>;
}
private handleFilterTextChange(event: React.ChangeEvent<HTMLInputElement>): void {
this.props.onFilterTextChanged(event.target.value);
}
}
export namespace SearchBar {
export interface Props {
filterText: string;
onFilterTextChanged(filterText: string): void;
}
export namespace Styles {
export const SEARCH_BAR_CLASS = 'search-bar';
}
}

View File

@ -0,0 +1,63 @@
import * as React from 'react';
// TODO: make this `async`.
// import { Async } from 'react-select/lib/Async';
import { BoardsService, Board } from '../../common/protocol/boards-service';
export class ConnectedBoards extends React.Component<ConnectedBoards.Props, ConnectedBoards.State> {
static TOOLBAR_ID: 'connected-boards-toolbar';
render(): React.ReactNode {
return <div className={ConnectedBoards.Styles.CONNECTED_BOARDS_CLASS}>
{this.select(this.state ? this.state.boards : undefined, this.state ? this.state.current : undefined)}
</div>;
}
componentDidMount(): void {
this.props.boardsService.connectedBoards().then(result => {
const { boards, current } = result;
this.setState({
boards,
current
})
});
}
private select(boards: Board[] | undefined, current: Board | undefined): React.ReactNode {
// Initial pessimistic.
const options = [<option>Loading...</option>];
if (boards) {
options.length = 0;
options.push(...boards.map(b => b.name).map(name => <option value={name} key={name}>{name}</option>));
}
const onChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
const current = (boards || []).find(board => board.name === event.target.value);
this.setState({ current });
};
return <select
onChange={onChange}
value={current ? current.name : 'Loading...'}
name={current ? current.name : 'Loading...'}
>
{options}
</select>
}
}
export namespace ConnectedBoards {
export interface Props {
readonly boardsService: BoardsService;
}
export interface State {
boards?: Board[];
current?: Board;
}
export namespace Styles {
export const CONNECTED_BOARDS_CLASS = 'connected-boards';
}
}

View File

@ -0,0 +1,63 @@
import { injectable } from 'inversify';
import { LanguageGrammarDefinitionContribution, TextmateRegistry } from '@theia/monaco/lib/browser/textmate';
@injectable()
export class ArduinoLanguageGrammarContribution implements LanguageGrammarDefinitionContribution {
static INO_LANGUAGE_ID = 'ino';
registerTextmateLanguage(registry: TextmateRegistry) {
monaco.languages.register({
id: ArduinoLanguageGrammarContribution.INO_LANGUAGE_ID,
extensions: ['.ino'],
aliases: ['INO', 'Ino', 'ino'],
});
monaco.languages.setLanguageConfiguration(ArduinoLanguageGrammarContribution.INO_LANGUAGE_ID, this.configuration);
const inoGrammar = require('../../../data/ino.tmLanguage.json');
registry.registerTextmateGrammarScope('source.ino', {
async getGrammarDefinition() {
return {
format: 'json',
content: inoGrammar
};
}
});
registry.mapLanguageIdToTextmateGrammar(ArduinoLanguageGrammarContribution.INO_LANGUAGE_ID, 'source.ino');
}
private readonly configuration: monaco.languages.LanguageConfiguration = {
comments: {
lineComment: '//',
blockComment: ['/*', '*/'],
},
brackets: [
['{', '}'],
['[', ']'],
['(', ')']
],
autoClosingPairs: [
{ open: '[', close: ']' },
{ open: '{', close: '}' },
{ open: '(', close: ')' },
{ open: '\'', close: '\'', notIn: ['string', 'comment'] },
{ open: '"', close: '"', notIn: ['string'] },
{ open: '/*', close: ' */', notIn: ['string'] }
],
surroundingPairs: [
{ open: '{', close: '}' },
{ open: '[', close: ']' },
{ open: '(', close: ')' },
{ open: '"', close: '"' },
{ open: '\'', close: '\'' },
],
folding: {
markers: {
start: new RegExp('^\\s*#pragma\\s+region\\b'),
end: new RegExp('^\\s*#pragma\\s+endregion\\b')
}
}
};
}

View File

@ -0,0 +1,16 @@
import { ListWidget } from './list-widget';
export class LibraryListWidget extends ListWidget {
static WIDGET_ID = 'library-list-widget';
static WIDGET_LABEL = 'Library Manager';
protected widgetProps(): ListWidget.Props {
return {
id: LibraryListWidget.WIDGET_ID,
title: LibraryListWidget.WIDGET_LABEL,
iconClass: 'fa fa-book' // TODO: find a better icon
}
}
}

View File

@ -0,0 +1,32 @@
import { injectable } from 'inversify';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
import { ListWidget } from './list-widget';
import { LibraryListWidget } from './library-list-widget';
@injectable()
export abstract class ListWidgetFrontendContribution extends AbstractViewContribution<ListWidget> implements FrontendApplicationContribution {
async initializeLayout(): Promise<void> {
await this.openView();
}
}
@injectable()
export class LibraryListWidgetFrontendContribution extends ListWidgetFrontendContribution {
constructor() {
super({
widgetId: LibraryListWidget.WIDGET_ID,
widgetName: LibraryListWidget.WIDGET_LABEL,
defaultWidgetOptions: {
area: 'left',
rank: 600
},
toggleCommandId: `${LibraryListWidget.WIDGET_ID}:toggle`,
toggleKeybinding: 'ctrlcmd+shift+l'
});
}
}

View File

@ -0,0 +1,72 @@
import * as React from 'react';
import { inject, injectable, postConstruct } from 'inversify';
import { Message } from '@phosphor/messaging';
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
import { WindowService } from '@theia/core/lib/browser/window/window-service';
import { FilterableListContainer } from '../components/component-list/filterable-list-container';
import { LibraryService } from '../../common/protocol/library-service';
@injectable()
export abstract class ListWidget extends ReactWidget {
@inject(LibraryService)
protected readonly libraryService: LibraryService;
@inject(WindowService)
protected readonly windowService: WindowService;
constructor() {
super();
const { id, title, iconClass } = this.widgetProps();
this.id = id;
this.title.label = title;
this.title.caption = title;
this.title.iconClass = iconClass;
this.title.closable = true;
this.addClass(ListWidget.Styles.LIST_WIDGET_CLASS);
this.node.tabIndex = 0; // To be able to set the focus on the widget.
}
protected abstract widgetProps(): ListWidget.Props;
@postConstruct()
protected init(): void {
this.update();
}
protected onActivateRequest(msg: Message): void {
super.onActivateRequest(msg);
this.node.focus();
this.render();
}
protected onUpdateRequest(msg: Message): void {
super.onUpdateRequest(msg);
this.render();
}
render(): React.ReactNode {
return <FilterableListContainer
service={this.libraryService}
windowService={this.windowService}
/>;
}
}
export namespace ListWidget {
/**
* Props for customizing the abstract list widget.
*/
export interface Props {
readonly id: string;
readonly title: string;
readonly iconClass: string;
}
export namespace Styles {
export const LIST_WIDGET_CLASS = 'arduino-list-widget'
}
}

View File

@ -0,0 +1 @@
@import './list-widget.css';

View File

@ -0,0 +1,76 @@
.arduino-list-widget {
color: var(--theia-ui-font-color1);
}
.arduino-list-widget .search-bar > input {
margin: 0px 5px 0px 5px;
width: 100%;
}
.component-list-item {
padding: 10px;
font-size: var(--theia-ui-font-size1);
}
.component-list-item:hover {
background: var(--theia-accent-color4);
cursor: pointer;
}
.component-list-item:hover .meta-info {
color: var(--theia-ui-font-color1);
}
.component-list-item .meta-info {
color: var(--theia-ui-font-color3);
}
.component-list-item .header {
padding-bottom: 2px;
display: flex;
}
.component-list-item .header .name {
font-weight: bold;
}
.component-list-item .header .author {
font-weight: bold;
color: var(--theia-ui-font-color2);
}
.component-list-item .header .version {
margin-left: auto;
justify-self: end;
color: var(--theia-ui-font-color2);
}
.component-list-item .header .installed {
margin-left: 4px;
justify-self: end;
background-color: var(--theia-accent-color2);
padding: 4px;
font-size: 10px;
font-weight: bold;
}
.component-list-item .footer {
display: flex;
}
.component-list-item .footer a {
color: var(--theia-brand-color1);
text-decoration: none;
font-weight: 600;
}
.component-list-item .footer .install {
margin-left: auto;
}
.component-list-item a:hover {
text-decoration: underline;
}
.component-list-item strong.installed {
color: rgb(0, 151, 157)
}

View File

@ -0,0 +1,19 @@
import { inject, injectable } from 'inversify';
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
import { WorkspaceServiceExt } from './workspace-service-ext';
/**
* This is a workaround to be able to inject the workspace service to the backend with its service path.
*/
@injectable()
export class WorkspaceServiceExtImpl implements WorkspaceServiceExt {
@inject(WorkspaceService)
protected readonly delegate: WorkspaceService;
async roots(): Promise<string[]> {
const stats = await this.delegate.roots;
return stats.map(stat => stat.uri);
}
}

View File

@ -0,0 +1,5 @@
export const WorkspaceServiceExtPath = '/services/workspace-service-ext';
export const WorkspaceServiceExt = Symbol('WorkspaceServiceExt');
export interface WorkspaceServiceExt {
roots(): Promise<string[]>;
}

View File

@ -0,0 +1,13 @@
export interface ArduinoComponent {
readonly name: string;
readonly author: string;
readonly summary: string;
readonly description: string;
readonly moreInfoLink?: string;
readonly availableVersions: string[];
readonly installable: boolean;
readonly installedVersion?: string;
}

View File

@ -0,0 +1,11 @@
import { ArduinoComponent } from "./arduino-component";
export const BoardsServicePath = '/services/boards-service';
export const BoardsService = Symbol('BoardsService');
export interface BoardsService {
connectedBoards(): Promise<{ boards: Board[], current?: Board }>;
search(options: { query?: string }): Promise<{ items: Board[] }>;
}
export interface Board extends ArduinoComponent {
}

View File

@ -0,0 +1,14 @@
export const CoreServicePath = '/services/core-service';
export const CoreService = Symbol('CoreService');
export interface CoreService {
compile(options: CoreService.Compile.Options): Promise<string>;
upload(): Promise<void>;
}
export namespace CoreService {
export namespace Compile {
export interface Options {
readonly uri: string;
}
}
}

View File

@ -0,0 +1,16 @@
import { ArduinoComponent } from "./arduino-component";
export const LibraryServicePath = '/services/library-service';
export const LibraryService = Symbol('LibraryService');
export interface LibraryService {
search(options: { query?: string }): Promise<{ items: Library[] }>;
}
export interface Library extends ArduinoComponent {
readonly builtIn?: boolean;
}
export namespace Library {
// TODO: figure out whether we need a dedicated `version` type.
export type Version = string;
}

View File

@ -0,0 +1,66 @@
import { ContainerModule } from 'inversify';
import { ArduinoDaemon } from './arduino-daemon';
import { ILogger } from '@theia/core/lib/common/logger';
import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';
import { LibraryService, LibraryServicePath } from '../common/protocol/library-service';
import { BoardsService, BoardsServicePath } from '../common/protocol/boards-service';
import { LibraryServiceImpl } from './library-service-impl';
import { BoardsServiceImpl } from './boards-service-impl';
import { CoreServiceImpl } from './core-service-impl';
import { CoreService, CoreServicePath } from '../common/protocol/core-service';
import { ConnectionContainerModule } from '@theia/core/lib/node/messaging/connection-container-module';
import { WorkspaceServiceExtPath, WorkspaceServiceExt } from '../browser/workspace-service-ext';
import { CoreClientProviderImpl } from './core-client-provider-impl';
import { CoreClientProviderPath, CoreClientProvider } from './core-client-provider';
export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(ArduinoDaemon).toSelf().inSingletonScope();
bind(BackendApplicationContribution).toService(ArduinoDaemon);
// Library service
const libraryServiceConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => {
bind(LibraryServiceImpl).toSelf().inSingletonScope();
bind(LibraryService).toService(LibraryServiceImpl);
bindBackendService(LibraryServicePath, LibraryService);
});
bind(ConnectionContainerModule).toConstantValue(libraryServiceConnectionModule);
// Boards service
const boardsServiceConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => {
bind(BoardsServiceImpl).toSelf().inSingletonScope();
bind(BoardsService).toService(BoardsServiceImpl);
bindBackendService(BoardsServicePath, BoardsService);
});
bind(ConnectionContainerModule).toConstantValue(boardsServiceConnectionModule);
// Arduino core client provider per Theia connection.
const coreClientProviderConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => {
bind(CoreClientProviderImpl).toSelf().inSingletonScope();
bind(CoreClientProvider).toService(CoreClientProviderImpl);
bindBackendService(CoreClientProviderPath, CoreClientProvider);
});
bind(ConnectionContainerModule).toConstantValue(coreClientProviderConnectionModule);
// Core service -> `verify` and `upload`. One per Theia connection.
const connectionConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => {
bind(CoreServiceImpl).toSelf().inSingletonScope();
bind(CoreService).toService(CoreServiceImpl);
bindBackendService(BoardsServicePath, BoardsService);
bindBackendService(CoreClientProviderPath, CoreClientProvider);
bindBackendService(CoreServicePath, CoreService);
});
bind(ConnectionContainerModule).toConstantValue(connectionConnectionModule);
// Bind the workspace service extension to the backend per Theia connection.
// So that we can access the workspace roots of the frontend.
const workspaceServiceExtConnectionModule = ConnectionContainerModule.create(({ bindFrontendService }) => {
bindFrontendService(WorkspaceServiceExtPath, WorkspaceServiceExt);
});
bind(ConnectionContainerModule).toConstantValue(workspaceServiceExtConnectionModule);
// Logger for the Arduino daemon
bind(ILogger).toDynamicValue(ctx => {
const parentLogger = ctx.container.get<ILogger>(ILogger);
return parentLogger.child('daemon');
}).inSingletonScope().whenTargetNamed('daemon');
});

View File

@ -0,0 +1,46 @@
import * as os from 'os';
import { exec } from 'child_process';
import { join, resolve } from 'path';
import { inject, injectable, named } from 'inversify';
import { ILogger } from '@theia/core/lib/common/logger';
import { BackendApplicationContribution } from '@theia/core/lib/node';
import { Deferred } from '@theia/core/lib/common/promise-util';
import { DaemonLog } from './daemon-log';
const EXECUTABLE_PATH = resolve(join(__dirname, '..', '..', 'build', `arduino-cli.${os.platform()}`))
@injectable()
export class ArduinoDaemon implements BackendApplicationContribution {
@inject(ILogger)
@named('daemon')
protected readonly logger: ILogger
protected isReady = new Deferred<boolean>();
async onStart() {
try {
const daemon = exec(`${EXECUTABLE_PATH} --debug daemon`, (err, stdout, stderr) => {
if (err || stderr) {
console.log(err || new Error(stderr));
return;
}
console.log(stdout);
});
if (daemon.stdout) {
daemon.stdout.on('data', data => DaemonLog.log(this.logger, data.toString()));
}
if (daemon.stderr) {
daemon.stderr.on('data', data => DaemonLog.log(this.logger, data.toString()));
}
if (daemon.stderr) {
daemon.on('exit', (code, signal) => DaemonLog.log(this.logger, `Daemon exited with code: ${code}. Signal was: ${signal}.`));
}
await new Promise((resolve, reject) => setTimeout(resolve, 2000));
this.isReady.resolve();
} catch (error) {
this.isReady.reject(error || new Error('failed to start arduino-cli'));
}
}
}

View File

@ -0,0 +1,37 @@
import { injectable, inject } from 'inversify';
import { BoardsService, Board } from '../common/protocol/boards-service';
import { PlatformSearchReq, PlatformSearchResp } from './cli-protocol/core_pb';
import { CoreClientProvider } from './core-client-provider';
@injectable()
export class BoardsServiceImpl implements BoardsService {
@inject(CoreClientProvider)
protected readonly coreClientProvider: CoreClientProvider;
async connectedBoards(): Promise<{ boards: Board[], current?: Board }> {
return { boards: [] };
}
async search(options: { query?: string }): Promise<{ items: Board[] }> {
let items: Board[] = [];
const { client, instance } = await this.coreClientProvider.getClient();
const req = new PlatformSearchReq();
req.setSearchArgs(options.query || "");
req.setInstance(instance);
const resp = await new Promise<PlatformSearchResp>((resolve, reject) => client.platformSearch(req, (err, resp) => (!!err ? reject : resolve)(!!err ? err : resp)));
items = resp.getSearchOutputList().map(o => <Board>{
name: o.getName(),
author: "Someone",
availableVersions: [],
description: "lorem ipsum sit dolor amet",
installable: false,
summary: "has none"
});
return { items };
}
}

View File

@ -0,0 +1,91 @@
import { inject, injectable } from 'inversify';
import * as grpc from 'grpc';
import { ArduinoCoreClient } from './cli-protocol/commands_grpc_pb';
import { InitResp, InitReq, Configuration, UpdateIndexReq, UpdateIndexResp } from './cli-protocol/commands_pb';
import { WorkspaceServiceExt } from '../browser/workspace-service-ext';
import { FileSystem } from '@theia/filesystem/lib/common';
import URI from '@theia/core/lib/common/uri';
import { CoreClientProvider, Client } from './core-client-provider';
@injectable()
export class CoreClientProviderImpl implements CoreClientProvider {
@inject(FileSystem)
protected readonly fileSystem: FileSystem;
@inject(WorkspaceServiceExt)
protected readonly workspaceServiceExt: WorkspaceServiceExt;
protected clients = new Map<string, Client>();
async getClient(workspaceRootOrResourceUri?: string): Promise<Client> {
const roots = await this.workspaceServiceExt.roots();
if (!workspaceRootOrResourceUri) {
return this.getOrCreateClient(roots[0]);
}
const root = roots
.sort((left, right) => right.length - left.length) // Longest "paths" first
.map(uri => new URI(uri))
.find(uri => uri.isEqualOrParent(new URI(workspaceRootOrResourceUri)));
if (!root) {
console.warn(`Could not retrieve the container workspace root for URI: ${workspaceRootOrResourceUri}.`);
console.warn(`Falling back to ${roots[0]}`);
return this.getOrCreateClient(roots[0]);
}
return this.getOrCreateClient(root.toString());
}
protected async getOrCreateClient(rootUri: string): Promise<Client> {
const existing = this.clients.get(rootUri);
if (existing) {
console.debug(`Reusing existing client for ${rootUri}.`);
return existing;
}
console.info(` >>> Creating and caching a new client for ${rootUri}...`);
const client = new ArduinoCoreClient('localhost:50051', grpc.credentials.createInsecure());
const config = new Configuration();
const path = await this.fileSystem.getFsPath(rootUri);
if (!path) {
throw new Error(`Could not resolve file-system path of URI: ${rootUri}.`);
}
config.setSketchbookdir(path);
config.setDatadir(path);
config.setDownloadsdir(path);
const initReq = new InitReq();
initReq.setConfiguration(config);
const initResp = await new Promise<InitResp>((resolve, reject) => client.init(initReq, (err, resp) => (!!err ? reject : resolve)(!!err ? err : resp)));
const instance = initResp.getInstance();
if (!instance) {
throw new Error(`Could not retrieve instance from the initialize response.`);
}
const updateReq = new UpdateIndexReq();
updateReq.setInstance(instance);
const updateResp = client.updateIndex(updateReq);
updateResp.on('data', (o: UpdateIndexResp) => {
const progress = o.getDownloadProgress();
if (progress) {
if (progress.getCompleted()) {
console.log(`Download${progress.getFile() ? ` of ${progress.getFile()}` : ''} completed.`);
} else {
console.log(`Downloading${progress.getFile() ? ` ${progress.getFile()}:` : ''} ${progress.getDownloaded()}.`);
}
}
});
// TODO: revisit this!!!
// `updateResp.on('data'` is called only when running, for instance, `compile`. It does not run eagerly.
// await new Promise<void>((resolve, reject) => {
// updateResp.on('close', resolve);
// updateResp.on('error', reject);
// });
const result = {
client,
instance
}
this.clients.set(rootUri, result);
console.info(` <<< New client has been successfully created and cached for ${rootUri}.`);
return result;
}
}

View File

@ -0,0 +1,13 @@
import { Instance } from './cli-protocol/common_pb';
import { ArduinoCoreClient } from './cli-protocol/commands_grpc_pb';
export const CoreClientProviderPath = '/services/core-client-provider';
export const CoreClientProvider = Symbol('CoreClientProvider');
export interface CoreClientProvider {
getClient(workspaceRootOrResourceUri?: string): Promise<Client>;
}
export interface Client {
readonly client: ArduinoCoreClient;
readonly instance: Instance;
}

View File

@ -0,0 +1,84 @@
import { inject, injectable } from 'inversify';
import { FileSystem } from '@theia/filesystem/lib/common/filesystem';
import { CoreService } from '../common/protocol/core-service';
import { CompileReq } from './cli-protocol/compile_pb';
import { BoardsService } from '../common/protocol/boards-service';
import { CoreClientProvider } from './core-client-provider';
import { PlatformInstallReq } from './cli-protocol/core_pb';
import { LibraryInstallReq } from './cli-protocol/lib_pb';
@injectable()
export class CoreServiceImpl implements CoreService {
@inject(CoreClientProvider)
protected readonly coreClientProvider: CoreClientProvider;
@inject(FileSystem)
protected readonly fileSystem: FileSystem;
@inject(BoardsService)
protected readonly boardsService: BoardsService;
async compile(options: CoreService.Compile.Options): Promise<string> {
console.log('compile', options);
const { uri } = options;
const sketchpath = await this.fileSystem.getFsPath(options.uri);
if (!sketchpath) {
throw new Error(`Cannot resolve FS path for URI: ${uri}.`);
}
const { client, instance } = await this.coreClientProvider.getClient(uri);
// const boards = await this.boardsService.connectedBoards();
// if (!boards.current) {
// throw new Error(`No selected board. The connected boards were: ${boards.boards}.`);
// }
// https://github.com/cmaglie/arduino-cli/blob/bd5e78701e7546787649d3cca6b21c5d22d0e438/cli/compile/compile.go#L78-L88
const installPlatformReq = new PlatformInstallReq();
installPlatformReq.setArchitecture('samd');
installPlatformReq.setVersion('1.6.0');
installPlatformReq.setInstance(instance);
const resp = client.platformInstall(installPlatformReq);
console.log(resp);
const installLibReq = new LibraryInstallReq();
installLibReq.setInstance(instance);
installLibReq.setName('arduino:samd');
const installResp = client.libraryInstall(installLibReq);
const xxx = await new Promise<string>((resolve, reject) => {
const chunks: Buffer[] = [];
installResp.on('data', (chunk: Buffer) => chunks.push(chunk));
installResp.on('error', error => reject(error));
installResp.on('end', () => resolve(Buffer.concat(chunks).toString('utf8').trim()))
});
console.log('xxx', xxx);
const compilerReq = new CompileReq();
compilerReq.setInstance(instance);
compilerReq.setSketchpath(sketchpath);
compilerReq.setFqbn('arduino:samd'/*boards.current.name*/);
// request.setShowproperties(false);
// request.setPreprocess(false);
// request.setBuildcachepath('');
// request.setBuildpath('');
// request.setBuildpropertiesList([]);
// request.setWarnings('none');
// request.setVerbose(true);
// request.setQuiet(false);
// request.setVidpid('');
// request.setExportfile('');
const result = client.compile(compilerReq);
return new Promise<string>((resolve, reject) => {
const chunks: Buffer[] = [];
result.on('data', (chunk: Buffer) => chunks.push(chunk));
result.on('error', error => reject(error));
result.on('end', () => resolve(Buffer.concat(chunks).toString('utf8').trim()))
});
}
upload(): Promise<void> {
throw new Error("Method not implemented.");
}
}

View File

@ -0,0 +1,67 @@
import { ILogger, LogLevel } from '@theia/core/lib/common/logger';
export interface DaemonLog {
readonly time: string;
readonly level: DaemonLog.Level;
readonly msg: string;
}
export namespace DaemonLog {
export type Level = 'info' | 'debug' | 'warning' | 'error';
export function is(arg: any | undefined): arg is DaemonLog {
return !!arg
&& typeof arg.time === 'string'
&& typeof arg.level === 'string'
&& typeof arg.msg === 'string'
}
export function toLogLevel(log: DaemonLog): LogLevel {
const { level } = log;
switch (level) {
case 'info': return LogLevel.INFO;
case 'debug': return LogLevel.DEBUG;
case 'error': return LogLevel.ERROR;
case 'warning': return LogLevel.WARN;
default: return LogLevel.INFO;
}
}
export function log(logger: ILogger, toLog: string): void {
const segments = toLog.split('time').filter(s => s.trim().length > 0);
for (const segment of segments) {
const maybeDaemonLog = parse(`time${segment}`.trim());
if (is(maybeDaemonLog)) {
const { msg } = maybeDaemonLog;
logger.log(toLogLevel(maybeDaemonLog), msg);
} else {
logger.info(toLog.trim());
}
}
}
// Super naive.
function parse(toLog: string): string | DaemonLog {
const rawSegments = toLog.split(/(\s+)/)
.map(segment => segment.replace(/['"]+/g, ''))
.map(segment => segment.trim())
.filter(segment => segment.length > 0);
const timeIndex = rawSegments.findIndex(segment => segment.startsWith('time='));
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) {
return {
time: rawSegments[timeIndex].split('=')[1],
level: rawSegments[levelIndex].split('=')[1] as Level,
msg: [rawSegments[msgIndex].split('=')[1], ...rawSegments.slice(msgIndex + 1)].join(' ')
}
}
// Otherwise, log the string as is.
return toLog;
}
}

View File

@ -0,0 +1,46 @@
import { injectable } from 'inversify';
import { Library, LibraryService } from '../common/protocol/library-service';
@injectable()
export class LibraryServiceImpl implements LibraryService {
async search(options: { query?: string; }): Promise<{ items: Library[] }> {
const { query } = options;
const allItems: Library[] = [
<Library>{
name: 'Keyboard',
availableVersions: ['1.0.0', '1.0.1', '1.02'],
author: 'Arduino',
summary: 'Allows an Arduino/Genuino board with USB capabilities to act as a Keyboard',
description: 'This library plugs on the HID library. It can be used with or without other HIG-based libraries (Mouse, Gamepad etc)',
installedVersion: '1.0.1',
moreInfoLink: 'https://www.arduino.cc/reference/en/language/functions/usb/keyboard/',
builtIn: true
},
<Library>{
name: 'Mouse',
availableVersions: ['1.0.0', '1.0.1'],
author: 'Arduino',
summary: 'Allows an Arduino board with USB capabilities to act as a Mouse. For Leonardo/Micro only',
description: 'This library plugs on the HID library. Can be used with ot without other HID-based libraries (Keyboard, Gamepad etc)',
installedVersion: '1.0.1',
moreInfoLink: 'https://www.arduino.cc/reference/en/language/functions/usb/mouse/',
builtIn: true
},
<Library>{
name: 'USBHost',
availableVersions: ['1.0.0', '1.0.1', '1.02', '1.0.3', '1.0.3', '1.0.4', '1.0.5'],
author: 'Arduino',
summary: 'Allows communication with USB peripherals like mice, keyboard, and thumbdrives.',
// tslint:disable-next-line:max-line-length
description: 'This USBHost library allows an Arduino Due board to appear as a USB host, enabling it to communicate with peripherals like USB mice and keyboards. USBHost does not support devices that ace corrected through USB hubs. This includes some keyboards that have an internal hub.',
moreInfoLink: 'https://www.arduino.cc/en/Reference/USBHost',
installable: true
}
];
return {
items: allItems.filter(item => !query || item.name.toLocaleLowerCase().indexOf(query.toLocaleLowerCase()) !== -1)
};
}
}

View File

@ -0,0 +1,16 @@
import * as grpc from "grpc";
import * as jspb from "google-protobuf";
export type GrpcMethod<Req, Resp> = (request: Req, callback: (error: grpc.ServiceError | null, response: Resp) => void) => grpc.ClientUnaryCall
export function promisify<M extends GrpcMethod<Req, Resp>, Req, Resp extends jspb.Message>(m: M, req: Req): Promise<Resp> {
return new Promise<Resp>((resolve, reject) => {
m(req, (err, resp) => {
if (!!err) {
reject(err);
} else {
resolve(resp);
}
});
});
}

View File

@ -0,0 +1,30 @@
{
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"noImplicitAny": true,
"noEmitOnError": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"strictNullChecks": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"module": "commonjs",
"moduleResolution": "node",
"target": "es5",
"outDir": "lib",
"lib": [
"es6",
"dom"
],
"jsx": "react",
"sourceMap": true,
"skipLibCheck": true
},
"include": [
"src"
],
"files": [
"../node_modules/@theia/monaco/src/typings/monaco/index.d.ts"
]
}

View File

@ -0,0 +1,38 @@
{
"rules": {
"class-name": true,
"comment-format": [true, "check-space"],
"curly": false,
"forin": false,
"indent": [true, "spaces"],
"max-line-length": [true, 180],
"no-trailing-whitespace": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"one-line": [true,
"check-open-brace",
"check-catch",
"check-else",
"check-whitespace"
],
"radix": true,
"trailing-comma": [false],
"triple-equals": [true, "allow-null-check"],
"typedef-whitespace": [true, {
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}],
"variable-name": false,
"whitespace": [true,
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type"
]
}
}

19
electron/.gitignore vendored Normal file
View File

@ -0,0 +1,19 @@
# The working-copy folder we use to package the application.
working-copy/
# Ignore all Theia generated things.
*.log
src-gen/
node_modules/
build/yarn.lock
webpack.config.js
lib/
# The electron-builder output.
dist/
# `dotenv` can provide dynamic input for the elecrton-builder. e.g.: commitish for the build.
electron-builder.env
# The generated `package.json` under the `build` folder.
build/package.json

48
electron/README.md Normal file
View File

@ -0,0 +1,48 @@
## Electron
All-in-one packager producing the `Arduino-PoC` Electron-based application.
## Prerequisites
The prerequisites are defined [here](https://github.com/theia-ide/theia/blob/master/doc/Developing.md#prerequisites).
### Build:
To build the Arduino-PoC Electron-based Theia application you have to do the followings:
```bash
yarn --cwd ./electron/packager/
```
The packaged application will be under the `./electron/build/dist` folder.
### CI:
The electron packager runs when:
- the build is manually triggered by the user, or
- on scheduled (CRON) jobs.
### Creating a Release Draft:
One can create a GitHub release draft, tag the source, and upload the artifacts to GitHub with Azure.
- Go to the Azure [build](https://dev.azure.com/typefox/Arduino/_build) page.
- Click on `Queue` in the top right corner.
- Set the branch to `master` or leave as is if it is already showing `master`.
- Add the `Release.Tag` pipeline variable and set the desired release version. Note, the version must start with `v` and we recommend naming tags that fit within [semantic versioning](https://semver.org).
![](static/azure-create-gh-release.jpg)
- Click on `Queue`.
- 🎈🎉
### Publishing the Release Draft:
One has to manually publish the GitHub release.
- Go to the [release page](https://github.com/TypeFox/arduino-poc/releases) of the arduino-poc repository.
- Select your release draft.
- Click on `Edit`.
![](static/edit-gh-release-draft.jpg)
- Optionally, you can adjust the release draft if you want.
![](static/publish-gh-release.jpg)
- Select `Publish release`.
- ✨

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 KiB

View File

@ -0,0 +1,76 @@
{
"name": "arduino-electron",
"description": "Arduino-PoC Electron",
"main": "src-gen/frontend/electron-main.js",
"author": "TypeFox",
"dependencies": {
"arduino-ide-extension": "file:../working-copy/arduino-ide-extension"
},
"devDependencies": {
"electron-builder": "^20.36.2"
},
"scripts": {
"build": "theia build --mode development",
"build:release": "theia build --mode production",
"package": "electron-builder --publish=never",
"package:preview": "electron-builder --dir"
},
"engines": {
"node": ">=8.12.0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/TypeFox/arduino-poc.git"
},
"build": {
"productName": "Arduino-PoC",
"appId": "arduino.PoC",
"asar": false,
"directories": {
"buildResources": "resources"
},
"files": [
"src-gen/**/*",
"lib/**/*",
"!node_modules/**/*.{ts,map}",
"!node_modules/**/*.spec.js",
"!node_modules/@theia/**/test/*",
"!node_modules/@theia/**/src/*",
"!node_modules/@theia/java/download",
"!node_modules/@theia/**/lib/*browser/*",
"!node_modules/@typefox/monaco-editor-core/*",
"!node_modules/oniguruma/*",
"!node_modules/onigasm/*"
],
"win": {
"target": [
"zip"
],
"artifactName": "${productName}-${env.ARDUINO_VERSION}-${os}.${ext}"
},
"mac": {
"target": [
"dmg"
],
"artifactName": "${productName}-${env.ARDUINO_VERSION}-${os}.${ext}",
"darkModeSupport": true
},
"dmg": {
"icon": "resources/icon.icns",
"iconSize": 128,
"contents": [
{
"x": 380,
"y": 240,
"type": "link",
"path": "/Applications"
},
{
"x": 122,
"y": 240,
"type": "file"
}
]
}
}
}

31
electron/packager/cli Executable file
View File

@ -0,0 +1,31 @@
#!/usr/bin/env node
// @ts-check
const { versionInfo } = require('./utils');
const yargs = require('yargs');
(() => {
yargs
.command({
command: 'name',
describe: 'Returns with the application name we build. The name includes the full application name with the version, the platform and the file extension.',
handler: () => {
const { platform } = process;
let ext = undefined;
let os = undefined;
if (platform === 'darwin') {
ext = 'dmg';
os = 'mac';
} else if (platform === 'win32') {
ext = 'zip';
os = 'win';
} else {
process.stderr.write(`Unexpected platform: ${platform}.`);
process.exit(1);
}
process.stdout.write(`Arduino-PoC-${versionInfo().version}-${os}.${ext}`);
process.exit(0);
}
})
.demandCommand(1)
.argv;
})();

188
electron/packager/index.js Normal file
View File

@ -0,0 +1,188 @@
//@ts-check
(async () => {
const fs = require('fs');
const join = require('path').join;
const shell = require('shelljs');
const utils = require('./utils');
const { version, release } = utils.versionInfo();
echo(`📦 Building ${release ? 'release ' : ''}version '${version}'...`);
const workingCopy = 'working-copy';
/**
* Relative path from the `__dirname` to the root where the `arduino-ide-extension` and the `arduino-ide-electron` folders are.
* This could come handy when moving the location of the `electron/packager`.
*/
const rootPath = join('..', '..');
// This is a HACK! We rename the root `node_modules` to something else. Otherwise, due to the hoisting,
// multiple Theia extensions will be picked up.
if (fs.existsSync(path(rootPath, 'node_modules'))) {
// We either do this or change the project structure.
echo('🔧 >>> [Hack] Renaming the root \'node_modules\' folder to \'.node_modules\'...');
mv('-f', path(rootPath, 'node_modules'), path(rootPath, '.node_modules'));
echo('👌 <<< [Hack] Renamed the root \'node_modules\' folder to \'.node_modules\'.')
}
//---------------------------+
// Clean the previous state. |
//---------------------------+
// rm -rf ../working-copy
rm('-rf', path('..', workingCopy));
// rm -rf ../build/dist
rm('-rf', path('..', 'build', 'dist'));
// rm -rf ../build/package.json
rm('-rf', path('..', 'build', 'package.json'));
//----------------------------------------------------------------------------------------------+
// Copy the following items into the `working-copy` folder. Make sure to reuse the `yarn.lock`. |
//----------------------------------------------------------------------------------------------+
mkdir('-p', path('..', workingCopy));
for (const name of ['arduino-ide-extension', 'arduino-ide-electron', 'yarn.lock', 'package.json', 'lerna.json']) {
cp('-rf', path(rootPath, name), path('..', workingCopy));
}
//-----------------------------------------------------+
// No need to build the `arduino-ide-browser` example. |
//-----------------------------------------------------+
//@ts-ignore
let pkg = require('../working-copy/package.json');
const workspaces = pkg.workspaces;
// We cannot remove the `arduino-ide-electron`. Otherwise, there is not way to collect the unused dependencies.
const dependenciesToRemove = ['arduino-ide-browser'];
for (const dependencyToRemove of dependenciesToRemove) {
const index = workspaces.indexOf(dependencyToRemove);
if (index !== -1) {
workspaces.splice(index, 1);
}
}
pkg.workspaces = workspaces;
fs.writeFileSync(path('..', workingCopy, 'package.json'), JSON.stringify(pkg, null, 2));
//-------------------------------------------------------------------------------------------------+
// Rebuild the extension with the copied `yarn.lock`. It is a must to use the same Theia versions. |
//-------------------------------------------------------------------------------------------------+
exec(`yarn --cwd ${path('..', workingCopy)}`, 'Building the Arduino Theia extensions');
// 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/arduino-ide-electron/');
//------------------------------------------------------------------------------------+
// Merge the `working-copy/package.json` with `electron/build/template-package.json`. |
//------------------------------------------------------------------------------------+
// @ts-ignore
pkg = require('../working-copy/arduino-ide-electron/package.json');
// @ts-ignore
const template = require('../build/template-package.json');
template.build.files = [ ...template.build.files, ...unusedDependencies.map(name => `!node_modules/${name}`) ];
pkg.dependencies = { ...pkg.dependencies, ...template.dependencies };
pkg.devDependencies = { ...pkg.devDependencies, ...template.devDependencies };
fs.writeFileSync(path('..', 'build', 'package.json'), JSON.stringify({
...pkg,
...template,
dependencies: pkg.dependencies,
devDependencies: pkg.devDependencies
}, null, 2));
echo(`📜 Effective 'package.json' for the Arduino-PoC application is:
-----------------------
${fs.readFileSync(path('..', 'build', 'package.json')).toString()}
-----------------------
`);
//-------------------------------------------------------------------------------------------+
// Install all private and public dependencies for the electron application and build Theia. |
//-------------------------------------------------------------------------------------------+
exec(`yarn --cwd ${path('..', 'build')}`, 'Installing dependencies');
exec(`yarn --cwd ${path('..', 'build')} build${release ? ':release' : ''}`, 'Building the Arduino-PoC application');
//------------------------------------------------------------------------------+
// 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 --cwd ${path('..', 'build')} package`, `Packaging your Arduino-PoC application`);
echo(`🎉 Success. Your application is at: ${path('..', 'build', 'dist')}`);
restore();
//--------+
// Utils. |
//--------+
function exec(command, toEcho) {
if (toEcho) {
echo(`⏱️ >>> ${toEcho}...`);
}
const { code, stderr, stdout } = shell.exec(command);
if (code !== 0) {
echo(`🔥 Error when executing ${command} => ${stderr}`);
shell.exit(1);
}
if (toEcho) {
echo(`👌 <<< ${toEcho}.`);
}
return stdout;
}
function cp(options, source, destination) {
shell.cp(options, source, destination);
assertNoError();
}
function rm(options, ...files) {
shell.rm(options, files);
assertNoError();
}
function mv(options, source, destination) {
shell.mv(options, source, destination);
assertNoError();
}
function mkdir(options, ...dir) {
shell.mkdir(options, dir);
assertNoError();
}
function echo(command) {
return shell.echo(command);
}
function assertNoError() {
const error = shell.error();
if (error) {
echo(error);
restore();
shell.exit(1);
}
}
function restore() {
if (fs.existsSync(path(rootPath, '.node_modules'))) {
echo('🔧 >>> [Restore] Renaming the root \'.node_modules\' folder to \'node_modules\'...');
mv('-f', path(rootPath, '.node_modules'), path(rootPath, 'node_modules'));
echo('👌 >>> [Restore] Renamed the root \'.node_modules\' folder to \'node_modules\'.');
}
}
/**
* Joins tha path from `__dirname`.
*/
function path(...paths) {
return join(__dirname, ...paths);
}
})();

View File

@ -0,0 +1,22 @@
{
"private": true,
"name": "packager",
"version": "1.0.0",
"description": "Packager for the Arduino-PoC electron application",
"main": "index.js",
"scripts": {
"package": "node index.js",
"cli": "./cli"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"depcheck": "^0.7.1",
"shelljs": "^0.8.3",
"yargs": "^12.0.5"
},
"engines": {
"node": ">=8.12.0"
}
}

100
electron/packager/utils.js Normal file
View File

@ -0,0 +1,100 @@
//@ts-check
const fs = require('fs');
const path = require('path');
const shell = require('shelljs');
const depcheck = require('depcheck');
/**
* Returns with the version info for the artifact.
* If the `RELEASE_TAG` environment variable is set, we us that.
* Falls back to the commit SHA if the `RELEASE_TAG` is the `$(Release.Tag)` string.
* Otherwise, we concatenate the version of the extracted from `theia-app/electron-app/package.json`
* and append the short commit SHA.
*/
function versionInfo() {
if (typeof process.env.RELEASE_TAG === 'undefined' || /* Azure -> */ process.env.RELEASE_TAG === '$(Release.Tag)') {
return {
version: `${targetVersion()}-${currentCommitish()}`,
release: false
}
} else {
return {
version: process.env.RELEASE_TAG,
release: true
}
}
}
/**
* Returns with the absolute path of the `theia-app/electron-app/`.
*/
function arduinoExtensionPath() {
// TODO: be smarter and locate the extension with Git: `git rev-parse --show-toplevel`.
return path.join(__dirname, '..', '..', 'arduino-ide-extension');
}
/**
* The version extracted from the `package.json` of the `arduino-ide-extension`. Falls back to `x.x.x`.
*/
function targetVersion() {
return JSON.parse(fs.readFileSync(path.join(arduinoExtensionPath(), 'package.json'), { encoding: 'utf8' })).version || 'x.x.x';
}
/**
* Returns with the trimmed result of the `git rev-parse --short HEAD` as the current commitish if `git` is on the `PATH`.
* Otherwise, it returns with `DEV_BUILD`.
*/
function currentCommitish() {
try {
const gitPath = shell.which('git');
const error = shell.error();
if (error) {
throw new Error(error);
}
const { stderr, stdout } = shell.exec(`"${gitPath}" rev-parse --short HEAD`, { silent: true });
if (stderr) {
throw new Error(stderr.toString().trim());
}
return stdout.toString().trim();
} catch (e) {
return 'DEV_BUILD';
}
}
/**
* 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);
});
})
}
module.exports = { versionInfo, collectUnusedDependencies };

723
electron/packager/yarn.lock Normal file
View File

@ -0,0 +1,723 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@babel/code-frame@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8"
integrity sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==
dependencies:
"@babel/highlight" "^7.0.0"
"@babel/generator@^7.2.2":
version "7.3.2"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.3.2.tgz#fff31a7b2f2f3dad23ef8e01be45b0d5c2fc0132"
integrity sha512-f3QCuPppXxtZOEm5GWPra/uYUjmNQlu9pbAD8D/9jze4pTY83rTtB1igTBSwvkeNlC5gR24zFFkz+2WHLFQhqQ==
dependencies:
"@babel/types" "^7.3.2"
jsesc "^2.5.1"
lodash "^4.17.10"
source-map "^0.5.0"
trim-right "^1.0.1"
"@babel/helper-function-name@^7.1.0":
version "7.1.0"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53"
integrity sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==
dependencies:
"@babel/helper-get-function-arity" "^7.0.0"
"@babel/template" "^7.1.0"
"@babel/types" "^7.0.0"
"@babel/helper-get-function-arity@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz#83572d4320e2a4657263734113c42868b64e49c3"
integrity sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==
dependencies:
"@babel/types" "^7.0.0"
"@babel/helper-split-export-declaration@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz#3aae285c0311c2ab095d997b8c9a94cad547d813"
integrity sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==
dependencies:
"@babel/types" "^7.0.0"
"@babel/highlight@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4"
integrity sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==
dependencies:
chalk "^2.0.0"
esutils "^2.0.2"
js-tokens "^4.0.0"
"@babel/parser@^7.2.2", "@babel/parser@^7.2.3", "@babel/parser@^7.3.1":
version "7.3.2"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.3.2.tgz#95cdeddfc3992a6ca2a1315191c1679ca32c55cd"
integrity sha512-QzNUC2RO1gadg+fs21fi0Uu0OuGNzRKEmgCxoLNzbCdoprLwjfmZwzUrpUNfJPaVRwBpDY47A17yYEGWyRelnQ==
"@babel/template@^7.1.0":
version "7.2.2"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.2.2.tgz#005b3fdf0ed96e88041330379e0da9a708eb2907"
integrity sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g==
dependencies:
"@babel/code-frame" "^7.0.0"
"@babel/parser" "^7.2.2"
"@babel/types" "^7.2.2"
"@babel/traverse@^7.2.3":
version "7.2.3"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.2.3.tgz#7ff50cefa9c7c0bd2d81231fdac122f3957748d8"
integrity sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw==
dependencies:
"@babel/code-frame" "^7.0.0"
"@babel/generator" "^7.2.2"
"@babel/helper-function-name" "^7.1.0"
"@babel/helper-split-export-declaration" "^7.0.0"
"@babel/parser" "^7.2.3"
"@babel/types" "^7.2.2"
debug "^4.1.0"
globals "^11.1.0"
lodash "^4.17.10"
"@babel/types@^7.0.0", "@babel/types@^7.2.2", "@babel/types@^7.3.2":
version "7.3.2"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.3.2.tgz#424f5be4be633fff33fb83ab8d67e4a8290f5a2f"
integrity sha512-3Y6H8xlUlpbGR+XvawiH0UXehqydTmNmEpozWcXymqwcrwYAl5KMvKtQ+TF6f6E08V6Jur7v/ykdDSF+WDEIXQ==
dependencies:
esutils "^2.0.2"
lodash "^4.17.10"
to-fast-properties "^2.0.0"
ansi-regex@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
ansi-regex@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
dependencies:
color-convert "^1.9.0"
argparse@^1.0.7:
version "1.0.10"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
dependencies:
sprintf-js "~1.0.2"
balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
builtin-modules@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.0.0.tgz#1e587d44b006620d90286cc7a9238bbc6129cab1"
integrity sha512-hMIeU4K2ilbXV6Uv93ZZ0Avg/M91RaKXucQ+4me2Do1txxBDyDZWCBa5bJSLqoNTRpXTLwEzIk1KmloenDDjhg==
camelcase@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42"
integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==
chalk@^2.0.0:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
dependencies:
ansi-styles "^3.2.1"
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
cliui@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49"
integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==
dependencies:
string-width "^2.1.1"
strip-ansi "^4.0.0"
wrap-ansi "^2.0.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"
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
dependencies:
color-name "1.1.3"
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=
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=
cross-spawn@^6.0.0:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
dependencies:
nice-try "^1.0.4"
path-key "^2.0.1"
semver "^5.5.0"
shebang-command "^1.2.0"
which "^1.2.9"
debug@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
dependencies:
ms "^2.1.1"
decamelize@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
depcheck@^0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/depcheck/-/depcheck-0.7.1.tgz#d4ef8511620fc5c783dafe27887cfdab533b1215"
integrity sha512-apCWM7uXYt7LZa5StGLY5UwNba0f6qMHA+gskcxti3PhcEfTMgqJMPW3CSSnRbVZb70QzbjLTESBCvZ+T00V2g==
dependencies:
"@babel/parser" "^7.3.1"
"@babel/traverse" "^7.2.3"
builtin-modules "^3.0.0"
deprecate "^1.0.0"
deps-regex "^0.1.4"
js-yaml "^3.4.2"
lodash "^4.17.11"
minimatch "^3.0.2"
please-upgrade-node "^3.1.1"
require-package-name "^2.0.1"
resolve "^1.10.0"
walkdir "0.0.12"
yargs "^12.0.1"
deprecate@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/deprecate/-/deprecate-1.1.0.tgz#bbd069d62b232175b4e8459b2650cd2bad51f4b8"
integrity sha512-b5dDNQYdy2vW9WXUD8+RQlfoxvqztLLhDE+T7Gd37I5E8My7nJkKu6FmhdDeRWJ8B+yjZKuwjCta8pgi8kgSqA==
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=
end-of-stream@^1.1.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==
dependencies:
once "^1.4.0"
escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
esprima@^4.0.0:
version "4.0.1"
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.2"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=
execa@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==
dependencies:
cross-spawn "^6.0.0"
get-stream "^4.0.0"
is-stream "^1.1.0"
npm-run-path "^2.0.0"
p-finally "^1.0.0"
signal-exit "^3.0.0"
strip-eof "^1.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"
integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
dependencies:
locate-path "^3.0.0"
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
get-caller-file@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a"
integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==
get-stream@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
dependencies:
pump "^3.0.0"
glob@^7.0.0:
version "7.1.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
globals@^11.1.0:
version "11.10.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-11.10.0.tgz#1e09776dffda5e01816b3bb4077c8b59c24eaa50"
integrity sha512-0GZF1RiPKU97IHUO5TORo9w1PwrH/NBPl+fS7oMLdaTRiYmYbwK4NWoZWrAdd0/abG9R2BU+OiwyQpTpE6pdfQ==
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
dependencies:
once "^1.3.0"
wrappy "1"
inherits@2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
interpret@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296"
integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==
invert-kv@^2.0.0:
version "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-fullwidth-code-point@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs=
dependencies:
number-is-nan "^1.0.0"
is-fullwidth-code-point@^2.0.0:
version "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-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
isexe@^2.0.0:
version "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.4.2:
version "3.12.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.1.tgz#295c8632a18a23e054cf5c9d3cecafe678167600"
integrity sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==
dependencies:
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==
lcid@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf"
integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==
dependencies:
invert-kv "^2.0.0"
locate-path@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
dependencies:
p-locate "^3.0.0"
path-exists "^3.0.0"
lodash@^4.17.10, lodash@^4.17.11:
version "4.17.11"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
map-age-cleaner@^0.1.1:
version "0.1.3"
resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a"
integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==
dependencies:
p-defer "^1.0.0"
mem@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/mem/-/mem-4.1.0.tgz#aeb9be2d21f47e78af29e4ac5978e8afa2ca5b8a"
integrity sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==
dependencies:
map-age-cleaner "^0.1.1"
mimic-fn "^1.0.0"
p-is-promise "^2.0.0"
mimic-fn@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==
minimatch@^3.0.2, 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==
dependencies:
brace-expansion "^1.1.7"
ms@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
nice-try@^1.0.4:
version "1.0.5"
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
npm-run-path@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=
dependencies:
path-key "^2.0.0"
number-is-nan@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
once@^1.3.0, once@^1.3.1, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
dependencies:
wrappy "1"
os-locale@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a"
integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==
dependencies:
execa "^1.0.0"
lcid "^2.0.0"
mem "^4.0.0"
p-defer@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c"
integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=
p-finally@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
p-is-promise@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.0.0.tgz#7554e3d572109a87e1f3f53f6a7d85d1b194f4c5"
integrity sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==
p-limit@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.1.0.tgz#1d5a0d20fb12707c758a655f6bbc4386b5930d68"
integrity sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==
dependencies:
p-try "^2.0.0"
p-locate@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
dependencies:
p-limit "^2.0.0"
p-try@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1"
integrity sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==
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-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"
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
path-key@^2.0.0, path-key@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
path-parse@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
please-upgrade-node@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.1.1.tgz#ed320051dfcc5024fae696712c8288993595e8ac"
integrity sha512-KY1uHnQ2NlQHqIJQpnh/i54rKkuxCEBx+voJIS/Mvb+L2iYd2NMotwduhKTMjfC1uKoX3VXOxLjIYG66dfJTVQ==
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"
integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
dependencies:
end-of-stream "^1.1.0"
once "^1.3.1"
rechoir@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=
dependencies:
resolve "^1.1.6"
require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
require-main-filename@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=
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@^1.1.6, resolve@^1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.0.tgz#3bdaaeaf45cc07f375656dfd2e54ed0810b101ba"
integrity sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==
dependencies:
path-parse "^1.0.6"
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:
version "5.6.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
set-blocking@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
shebang-command@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=
dependencies:
shebang-regex "^1.0.0"
shebang-regex@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
shelljs@^0.8.3:
version "0.8.3"
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.3.tgz#a7f3319520ebf09ee81275b2368adb286659b097"
integrity sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==
dependencies:
glob "^7.0.0"
interpret "^1.0.0"
rechoir "^0.6.2"
signal-exit@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
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"
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
string-width@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
dependencies:
code-point-at "^1.0.0"
is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0"
string-width@^2.0.0, string-width@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
dependencies:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"
strip-ansi@^3.0.0, strip-ansi@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
dependencies:
ansi-regex "^2.0.0"
strip-ansi@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
dependencies:
ansi-regex "^3.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"
integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
supports-color@^5.3.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
dependencies:
has-flag "^3.0.0"
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=
trim-right@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=
walkdir@0.0.12:
version "0.0.12"
resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.0.12.tgz#2f24f1ade64aab1e458591d4442c8868356e9281"
integrity sha512-HFhaD4mMWPzFSqhpyDG48KDdrjfn409YQuVW7ckZYhW4sE87mYtWifdB/+73RA7+p4s4K18n5Jfx1kHthE1gBw==
which-module@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
which@^1.2.9:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
dependencies:
isexe "^2.0.0"
wrap-ansi@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=
dependencies:
string-width "^1.0.1"
strip-ansi "^3.0.1"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
"y18n@^3.2.1 || ^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
yargs-parser@^11.1.1:
version "11.1.1"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4"
integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==
dependencies:
camelcase "^5.0.0"
decamelize "^1.2.0"
yargs@^12.0.1, yargs@^12.0.5:
version "12.0.5"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13"
integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==
dependencies:
cliui "^4.0.0"
decamelize "^1.2.0"
find-up "^3.0.0"
get-caller-file "^1.0.1"
os-locale "^3.0.0"
require-directory "^2.1.1"
require-main-filename "^1.0.1"
set-blocking "^2.0.0"
string-width "^2.0.0"
which-module "^2.0.0"
y18n "^3.2.1 || ^4.0.0"
yargs-parser "^11.1.1"

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

11
lerna.json Normal file
View File

@ -0,0 +1,11 @@
{
"lerna": "2.4.0",
"version": "independent",
"useWorkspaces": true,
"npmClient": "yarn",
"command": {
"run": {
"stream": true
}
}
}

22
package.json Normal file
View File

@ -0,0 +1,22 @@
{
"name": "arduino-poc",
"version": "0.0.1",
"description": "A proof-of-concent demonstrating an Arduino IDE built using Theia",
"main": "index.js",
"repository": "https://github.com/TypeFox/arduino-poc.git",
"author": "Christian Weichel <christian.weichel@typefox.io>",
"license": "MIT",
"private": true,
"devDependencies": {
"lerna": "^3.13.3"
},
"scripts": {
"prepare": "lerna run prepare",
"start": "cd arduino-ide-browser && yarn start"
},
"workspaces": [
"arduino-ide-electron",
"arduino-ide-browser",
"arduino-ide-extension"
]
}

11533
yarn.lock Normal file

File diff suppressed because it is too large Load Diff