Browse Source

Added tslint and fixed all lint issues

update-deps
Alex Mikhalev 8 years ago
parent
commit
ba4ee33792
  1. 70
      app/script/App.tsx
  2. 4
      app/script/index.tsx
  3. 161
      app/script/mqtt.ts
  4. 60
      app/script/paho-mqtt.d.ts
  5. 62
      app/script/sprinklers.ts
  6. 29
      app/style/app.css
  7. 7
      package.json
  8. 1
      tsconfig.json
  9. 25
      tslint.json

70
app/script/App.tsx

@ -6,30 +6,41 @@ import FontAwesome = require("react-fontawesome");
import * as classNames from "classnames"; import * as classNames from "classnames";
import "semantic-ui-css/semantic.css"; import "semantic-ui-css/semantic.css";
import "font-awesome/css/font-awesome.css" import "font-awesome/css/font-awesome.css";
import "app/style/app.css"; import "app/style/app.css";
/* tslint:disable:object-literal-sort-keys */
@observer @observer
class SectionTable extends React.PureComponent<{ sections: Section[] }, void> { class SectionTable extends React.PureComponent<{ sections: Section[] }, void> {
static renderRow(section: Section, index: number) { private static renderRow(section: Section, index: number) {
const { name, state } = section; const { name, state } = section;
return ( return (
<Table.Row key={index}> <Table.Row key={index}>
<Table.Cell className="section--name">Section {name}</Table.Cell> <Table.Cell className="section--number">{"" + (index + 1)}</Table.Cell>
<Table.Cell className="section--state">State: {state + ""}</Table.Cell> <Table.Cell className="section--name">{name}</Table.Cell>
<Table.Cell className={classNames({
"section--state": true,
"section--state-true": state,
"section--state-false": !state,
})}>{state ?
(<span><FontAwesome name="tint" /> Irrigating</span>)
: "Not irrigating"}
</Table.Cell>
</Table.Row> </Table.Row>
); );
} }
render() { public render() {
return (<Table celled striped> return (<Table celled striped>
<Table.Header> <Table.Header>
<Table.Row> <Table.Row>
<Table.HeaderCell colSpan="3">Sections</Table.HeaderCell> <Table.HeaderCell colSpan="3">Sections</Table.HeaderCell>
</Table.Row> </Table.Row>
<Table.Row> <Table.Row>
<Table.HeaderCell>Name</Table.HeaderCell> <Table.HeaderCell className="section--number">#</Table.HeaderCell>
<Table.HeaderCell>State</Table.HeaderCell> <Table.HeaderCell className="section--name">Name</Table.HeaderCell>
<Table.HeaderCell className="section--state">State</Table.HeaderCell>
</Table.Row> </Table.Row>
</Table.Header> </Table.Header>
<Table.Body> <Table.Body>
@ -44,26 +55,28 @@ class SectionTable extends React.PureComponent<{ sections: Section[] }, void> {
@observer @observer
class ProgramTable extends React.PureComponent<{ programs: Program[] }, void> { class ProgramTable extends React.PureComponent<{ programs: Program[] }, void> {
static renderRow(program: Program, i: number) { private static renderRow(program: Program, i: number) {
const { name, running } = program; const { name, running } = program;
return ( return (
<Table.Row key={i}> <Table.Row key={i}>
<Table.Cell className="program--name">Program {name}</Table.Cell> <Table.Cell className="program--number">{"" + (i + 1)}</Table.Cell>
<Table.Cell className="program--running">running: {running + ""}</Table.Cell> <Table.Cell className="program--name">{name}</Table.Cell>
<Table.Cell className="program--running">{running ? "Running" : "Not running"}</Table.Cell>
</Table.Row> </Table.Row>
); );
} }
render() { public render() {
return ( return (
<Table celled striped> <Table celled>
<Table.Header> <Table.Header>
<Table.Row> <Table.Row>
<Table.HeaderCell colSpan="3">Programs</Table.HeaderCell> <Table.HeaderCell colSpan="3">Programs</Table.HeaderCell>
</Table.Row> </Table.Row>
<Table.Row> <Table.Row>
<Table.HeaderCell>Name</Table.HeaderCell> <Table.HeaderCell className="program--number">#</Table.HeaderCell>
<Table.HeaderCell>Running</Table.HeaderCell> <Table.HeaderCell className="program--name">Name</Table.HeaderCell>
<Table.HeaderCell className="program--running">Running</Table.HeaderCell>
</Table.Row> </Table.Row>
</Table.Header> </Table.Header>
<Table.Body> <Table.Body>
@ -76,9 +89,20 @@ class ProgramTable extends React.PureComponent<{ programs: Program[] }, void> {
} }
} }
const ConnectionState = ({ connected }: { connected: boolean }) =>
<span className={classNames({
"device--connectionState": true,
"device--connectionState-connected": connected,
"device--connectionState-disconnected": !connected,
})}>
<FontAwesome name={connected ? "plug" : "chain-broken"} />
&nbsp;
{connected ? "Connected" : "Disconnected"}
</span>;
@observer @observer
class DeviceView extends React.PureComponent<{ device: SprinklersDevice }, void> { class DeviceView extends React.PureComponent<{ device: SprinklersDevice }, void> {
render() { public render() {
const { id, connected, sections, programs } = this.props.device; const { id, connected, sections, programs } = this.props.device;
return ( return (
<Item> <Item>
@ -86,15 +110,7 @@ class DeviceView extends React.PureComponent<{ device: SprinklersDevice }, void>
<Item.Content> <Item.Content>
<Header as="h1"> <Header as="h1">
<span>Device </span><kbd>{id}</kbd> <span>Device </span><kbd>{id}</kbd>
<small className={classNames({ <ConnectionState connected={connected} />
"device--connectedState": true,
"device--connectedState-connected": connected,
"device--connectedState-disconnected": !connected
})}>
<FontAwesome name={connected ? "plug" : "chain-broken"} />
&nbsp;
{connected ? "Connected" : "Disconnected"}
</small>
</Header> </Header>
<Item.Meta> <Item.Meta>
@ -109,7 +125,7 @@ class DeviceView extends React.PureComponent<{ device: SprinklersDevice }, void>
@observer @observer
export default class App extends React.PureComponent<{ device: SprinklersDevice }, any> { export default class App extends React.PureComponent<{ device: SprinklersDevice }, any> {
render() { public render() {
return <Item.Group divided><DeviceView device={this.props.device} /></Item.Group> return <Item.Group divided><DeviceView device={this.props.device} /></Item.Group>;
} }
} }

4
app/script/index.tsx

@ -21,5 +21,5 @@ if (module.hot) {
ReactDOM.render(<AppContainer> ReactDOM.render(<AppContainer>
<NextApp device={device} /> <NextApp device={device} />
</AppContainer>, rootElem); </AppContainer>, rootElem);
}) });
} }

161
app/script/mqtt.ts

@ -1,47 +1,48 @@
/// <reference path="./paho-mqtt.d.ts" />
import "paho-mqtt/mqttws31"; import "paho-mqtt/mqttws31";
import MQTT = Paho.MQTT; import MQTT = Paho.MQTT;
import { EventEmitter } from "events"; import { EventEmitter } from "events";
import { SprinklersDevice, SprinklersApi, Section, Program } from "./sprinklers"; import * as objectAssign from "object-assign";
import {
SprinklersDevice, SprinklersApi, Section, Program, ProgramItem, Schedule, TimeOfDay, Weekday,
} from "./sprinklers";
export class MqttApiClient extends EventEmitter implements SprinklersApi { export class MqttApiClient extends EventEmitter implements SprinklersApi {
client: MQTT.Client private static newClientId() {
return "sprinklers3-MqttApiClient-" + Math.round(Math.random() * 1000);
}
connected: boolean public client: MQTT.Client;
devices: { [prefix: string]: MqttSprinklersDevice } = {}; public connected: boolean;
public devices: { [prefix: string]: MqttSprinklersDevice } = {};
constructor() { constructor() {
super(); super();
this.client = new MQTT.Client(location.hostname, 1884, MqttApiClient.newClientId()); this.client = new MQTT.Client(location.hostname, 1884, MqttApiClient.newClientId());
this.client.onMessageArrived = m => this.onMessageArrived(m); this.client.onMessageArrived = (m) => this.onMessageArrived(m);
this.client.onConnectionLost = e => this.onConnectionLost(e); this.client.onConnectionLost = (e) => this.onConnectionLost(e);
}
static newClientId() {
return "sprinklers3-MqttApiClient-" + Math.round(Math.random() * 1000);
} }
start() { public start() {
console.log("connecting to mqtt with client id %s", this.client.clientId); console.log("connecting to mqtt with client id %s", this.client.clientId);
this.client.connect({ this.client.connect({
onFailure: (e) => { onFailure: (e) => {
console.log("mqtt error: ", e.errorMessage); console.log("mqtt error: ", e.errorMessage);
}, },
onSuccess: () => { onSuccess: () => {
console.log("mqtt connected") console.log("mqtt connected");
this.connected = true; this.connected = true;
for (const prefix in this.devices) { for (const prefix of Object.keys(this.devices)) {
const device = this.devices[prefix]; const device = this.devices[prefix];
device.doSubscribe(); device.doSubscribe();
} }
} },
}) });
} }
getDevice(prefix: string): SprinklersDevice { public getDevice(prefix: string): SprinklersDevice {
if (/\//.test(prefix)) { if (/\//.test(prefix)) {
throw new Error("Prefix cannot contain a /"); throw new Error("Prefix cannot contain a /");
} }
@ -54,16 +55,18 @@ export class MqttApiClient extends EventEmitter implements SprinklersApi {
return this.devices[prefix]; return this.devices[prefix];
} }
removeDevice(prefix: string) { public removeDevice(prefix: string) {
const device = this.devices[prefix]; const device = this.devices[prefix];
if (!device) return; if (!device) {
return;
}
device.doUnsubscribe(); device.doUnsubscribe();
delete this.devices[prefix]; delete this.devices[prefix];
} }
private onMessageArrived(m: MQTT.Message) { private onMessageArrived(m: MQTT.Message) {
// console.log("message arrived: ", m) // console.log("message arrived: ", m)
const topicIdx = m.destinationName.indexOf('/'); // find the first / const topicIdx = m.destinationName.indexOf("/"); // find the first /
const prefix = m.destinationName.substr(0, topicIdx); // assume prefix does not contain a / const prefix = m.destinationName.substr(0, topicIdx); // assume prefix does not contain a /
const topic = m.destinationName.substr(topicIdx + 1); const topic = m.destinationName.substr(topicIdx + 1);
const device = this.devices[prefix]; const device = this.devices[prefix];
@ -80,8 +83,8 @@ export class MqttApiClient extends EventEmitter implements SprinklersApi {
} }
class MqttSprinklersDevice extends SprinklersDevice { class MqttSprinklersDevice extends SprinklersDevice {
readonly apiClient: MqttApiClient; public readonly apiClient: MqttApiClient;
readonly prefix: string; public readonly prefix: string;
constructor(apiClient: MqttApiClient, prefix: string) { constructor(apiClient: MqttApiClient, prefix: string) {
super(); super();
@ -89,27 +92,16 @@ class MqttSprinklersDevice extends SprinklersDevice {
this.prefix = prefix; this.prefix = prefix;
} }
private getSubscriptions() { public doSubscribe() {
return [
`${this.prefix}/connected`,
`${this.prefix}/sections`,
`${this.prefix}/sections/+/#`,
`${this.prefix}/programs`,
`${this.prefix}/programs/+/#`
];
}
doSubscribe() {
const c = this.apiClient.client; const c = this.apiClient.client;
this.getSubscriptions() this.getSubscriptions()
.forEach(filter => c.subscribe(filter, { qos: 1 })); .forEach((filter) => c.subscribe(filter, { qos: 1 }));
} }
doUnsubscribe() { public doUnsubscribe() {
const c = this.apiClient.client; const c = this.apiClient.client;
this.getSubscriptions() this.getSubscriptions()
.forEach(filter => c.unsubscribe(filter)); .forEach((filter) => c.unsubscribe(filter));
} }
/** /**
@ -117,78 +109,119 @@ class MqttSprinklersDevice extends SprinklersDevice {
* @param topic The topic, with prefix removed * @param topic The topic, with prefix removed
* @param payload The payload string * @param payload The payload string
*/ */
onMessage(topic: string, payload: string) { public onMessage(topic: string, payload: string) {
var matches; if (topic === "connected") {
if (topic == "connected") { this.connected = (payload === "true");
this.connected = (payload == "true");
// console.log(`MqttSprinklersDevice with prefix ${this.prefix}: ${this.connected}`) // console.log(`MqttSprinklersDevice with prefix ${this.prefix}: ${this.connected}`)
} else if ((matches = topic.match(/^sections(?:\/(\d+)(?:\/?(.+))?)?$/)) != null) { return;
const [topic, secStr, subTopic] = matches; }
let matches = topic.match(/^sections(?:\/(\d+)(?:\/?(.+))?)?$/);
if (matches != null) {
const [_topic, secStr, subTopic] = matches;
// console.log(`section: ${secStr}, topic: ${subTopic}, payload: ${payload}`); // console.log(`section: ${secStr}, topic: ${subTopic}, payload: ${payload}`);
if (!secStr) { // new number of sections if (!secStr) { // new number of sections
this.sections = new Array(Number(payload)); this.sections = new Array(Number(payload));
} else { } else {
const secNum = Number(secStr); const secNum = Number(secStr);
var section = this.sections[secNum]; let section = this.sections[secNum];
if (!section) { if (!section) {
this.sections[secNum] = section = new MqttSection(); this.sections[secNum] = section = new MqttSection();
} }
(section as MqttSection).onMessage(subTopic, payload); (section as MqttSection).onMessage(subTopic, payload);
} }
} else if ((matches = topic.match(/^programs(?:\/(\d+)(?:\/?(.+))?)?$/)) != null) { return;
const [topic, progStr, subTopic] = matches; }
matches = topic.match(/^programs(?:\/(\d+)(?:\/?(.+))?)?$/);
if (matches != null) {
const [_topic, progStr, subTopic] = matches;
// console.log(`program: ${progStr}, topic: ${subTopic}, payload: ${payload}`); // console.log(`program: ${progStr}, topic: ${subTopic}, payload: ${payload}`);
if (!progStr) { // new number of programs if (!progStr) { // new number of programs
this.programs = new Array(Number(payload)); this.programs = new Array(Number(payload));
} else { } else {
const progNum = Number(progStr); const progNum = Number(progStr);
var program = this.programs[progNum]; let program = this.programs[progNum];
if (!program) { if (!program) {
this.programs[progNum] = program = new MqttProgram(); this.programs[progNum] = program = new MqttProgram();
} }
(program as MqttProgram).onMessage(subTopic, payload); (program as MqttProgram).onMessage(subTopic, payload);
} }
} else { } else {
console.warn(`MqttSprinklersDevice recieved invalid topic: ${topic}`) console.warn(`MqttSprinklersDevice recieved invalid topic: ${topic}`);
} }
} }
get id(): string { get id(): string {
return this.prefix; return this.prefix;
} }
private getSubscriptions() {
return [
`${this.prefix}/connected`,
`${this.prefix}/sections`,
`${this.prefix}/sections/+/#`,
`${this.prefix}/programs`,
`${this.prefix}/programs/+/#`,
];
}
} }
interface SectionJSON { interface ISectionJSON {
name: string; name: string;
pin: number; pin: number;
} }
class MqttSection extends Section { class MqttSection extends Section {
onMessage(topic: string, payload: string) { public onMessage(topic: string, payload: string) {
if (topic == "state") { if (topic === "state") {
this.state = (payload == "true"); this.state = (payload === "true");
} else if (topic == null) { } else if (topic == null) {
const json = JSON.parse(payload) as SectionJSON; const json = JSON.parse(payload) as ISectionJSON;
this.name = json.name; this.name = json.name;
} }
} }
} }
interface ProgramJSON { interface IScheduleJSON {
times: TimeOfDay[];
weekdays: number[];
from?: string;
to?: string;
}
function scheduleFromJSON(json: IScheduleJSON): Schedule {
const sched = new Schedule();
sched.times = json.times;
sched.weekdays = json.weekdays;
sched.from = json.from == null ? null : new Date(json.from);
sched.to = json.to == null ? null : new Date(json.to);
return sched;
}
interface IProgramJSON {
name: string; name: string;
enabled: boolean; enabled: boolean;
// sequence: Array<ProgramItem>; sequence: ProgramItem[];
// sched: Schedule; sched: IScheduleJSON;
} }
class MqttProgram extends Program { class MqttProgram extends Program {
onMessage(topic: string, payload: string) { public onMessage(topic: string, payload: string) {
if (topic == "running") { if (topic === "running") {
this.running = (payload == "true"); this.running = (payload === "true");
} else if (topic == null) { } else if (topic == null) {
const json = JSON.parse(payload) as Partial<ProgramJSON>; const json = JSON.parse(payload) as Partial<IProgramJSON>;
this.name = json.name; if (json.name != null) {
this.enabled = json.enabled; this.name = json.name;
}
if (json.enabled != null) {
this.enabled = json.enabled;
}
if (json.sequence != null) {
this.sequence = json.sequence;
}
if (json.sched != null) {
this.schedule = scheduleFromJSON(json.sched);
}
} }
} }
} }

60
app/script/paho-mqtt.d.ts vendored

@ -1,9 +1,11 @@
/* tslint:disable:interface-name */
declare namespace Paho { declare namespace Paho {
namespace MQTT { namespace MQTT {
interface MQTTError { errorCode: string, errorMessage: string } interface MQTTError { errorCode: string; errorMessage: string; }
interface WithInvocationContext { invocationContext: object } interface WithInvocationContext { invocationContext: object; }
interface ErrorWithInvocationContext extends MQTTError, WithInvocationContext {} interface ErrorWithInvocationContext extends MQTTError, WithInvocationContext {}
interface OnSubscribeSuccessParams extends WithInvocationContext { grantedQos: number } interface OnSubscribeSuccessParams extends WithInvocationContext { grantedQos: number; }
type OnConnectionLostHandler = (error: MQTTError) => void; type OnConnectionLostHandler = (error: MQTTError) => void;
type OnMessageHandler = (message: Message) => void; type OnMessageHandler = (message: Message) => void;
interface ConnectionOptions { interface ConnectionOptions {
@ -18,8 +20,8 @@ declare namespace Paho {
onSuccess?: (o: WithInvocationContext) => void; onSuccess?: (o: WithInvocationContext) => void;
mqttVersion?: number; mqttVersion?: number;
onFailure?: (e: ErrorWithInvocationContext) => void; onFailure?: (e: ErrorWithInvocationContext) => void;
hosts?: Array<string>; hosts?: string[];
ports?: Array<number>; ports?: number[];
} }
interface SubscribeOptions { interface SubscribeOptions {
qos?: number; qos?: number;
@ -35,41 +37,41 @@ declare namespace Paho {
timeout?: number; timeout?: number;
} }
class Client { class Client {
public readonly clientId: string;
public readonly host: string;
public readonly path: string;
public readonly port: number;
public onConnectionLost: OnConnectionLostHandler;
public onMessageArrived: OnMessageHandler;
public onMessageDelivered: OnMessageHandler;
// tslint:disable unified-signatures
constructor(host: string, port: number, path: string, clientId: string); constructor(host: string, port: number, path: string, clientId: string);
constructor(host: string, port: number, clientId: string); constructor(host: string, port: number, clientId: string);
constructor(hostUri: string, clientId: string); constructor(hostUri: string, clientId: string);
readonly clientId: string; public connect(connectionOptions?: ConnectionOptions);
readonly host: string; public disconnect();
readonly path: string;
readonly port: number;
onConnectionLost: OnConnectionLostHandler;
onMessageArrived: OnMessageHandler;
onMessageDelivered: OnMessageHandler;
connect(connectionOptions?: ConnectionOptions);
disconnect();
getTraceLog(): Object[]; public getTraceLog(): object[];
startTrace(); public startTrace();
stopTrace(); public stopTrace();
send(message: Message); public send(message: Message);
subscribe(filter: string, subcribeOptions?: SubscribeOptions); public subscribe(filter: string, subcribeOptions?: SubscribeOptions);
unsubscribe(filter: string, unsubcribeOptions?: UnsubscribeOptions); public unsubscribe(filter: string, unsubcribeOptions?: UnsubscribeOptions);
} }
class Message { class Message {
constructor(payload: String | ArrayBuffer); public destinationName: string;
public readonly duplicate: boolean;
public readonly payloadBytes: ArrayBuffer;
public readonly payloadString: string;
public qos: number;
public retained: boolean;
destinationName: string; constructor(payload: string | ArrayBuffer);
readonly duplicate: boolean;
readonly payloadBytes: ArrayBuffer;
readonly payloadString: string;
qos: number;
retained: boolean;
} }
} }
} }

62
app/script/sprinklers.ts

@ -2,69 +2,69 @@ import { observable } from "mobx";
export class Section { export class Section {
@observable @observable
name: string = "" public name: string = "";
@observable @observable
state: boolean = false public state: boolean = false;
} }
class TimeOfDay { export interface ITimeOfDay {
hour: number hour: number;
minute: number minute: number;
second: number second: number;
millisecond: number millisecond: number;
} }
enum Weekday { export enum Weekday {
Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday,
} }
class Schedule { export class Schedule {
times: TimeOfDay[] = []; public times: ITimeOfDay[] = [];
weekdays: Weekday[] = []; public weekdays: Weekday[] = [];
from?: Date = null; public from?: Date = null;
to?: Date = null; public to?: Date = null;
} }
class ProgramItem { export interface IProgramItem {
section: number = -1; // the section number
// duration in milliseconds section: number;
duration: number = 0; // duration in seconds
duration: number;
} }
export class Program { export class Program {
@observable @observable
name: string = "" public name: string = "";
@observable @observable
enabled: boolean = false public enabled: boolean = false;
@observable @observable
schedule: Schedule = new Schedule() public schedule: Schedule = new Schedule();
@observable @observable
sequence: Array<ProgramItem> = []; public sequence: IProgramItem[] = [];
@observable @observable
running: boolean = false; public running: boolean = false;
} }
export abstract class SprinklersDevice { export abstract class SprinklersDevice {
@observable @observable
connected: boolean = false; public connected: boolean = false;
@observable @observable
sections: Array<Section> = []; public sections: Section[] = [];
@observable @observable
programs: Array<Program> = []; public programs: Program[] = [];
abstract get id(): string; abstract get id(): string;
} }
export interface SprinklersApi { export interface ISprinklersApi {
start(); start();
getDevice(id: string) : SprinklersDevice; getDevice(id: string): SprinklersDevice;
removeDevice(id: string) removeDevice(id: string);
} }

29
app/style/app.css

@ -1,19 +1,36 @@
.device--connectedState-connected { .device--connectionState {
margin-left: 10px;
font-size: 18px;
font-weight: 100;
}
.device--connectionState-connected {
color: #13D213; color: #13D213;
} }
.device--connectedState-disconnected { .device--connectionState-disconnected {
color: #D20000; color: #D20000;
} }
.section--name { .section--number,
width: 200px; .program--number {
width: 20px
}
.section--name,
.program--name {
width: 150px;
white-space: nowrap;
} }
.section--state { .section--state {
} }
.program--name { .section--state-true {
width: 200px; color: green;
}
.section--state-false {
} }

7
package.json

@ -8,7 +8,8 @@
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"clean": "rm -rf ./dist ./build", "clean": "rm -rf ./dist ./build",
"build": "webpack --config ./webpack/prod.config.js", "build": "webpack --config ./webpack/prod.config.js",
"start:dev": "webpack-dev-server --config ./webpack/dev.config.js" "start:dev": "webpack-dev-server --config ./webpack/dev.config.js",
"lint": "tslint app/script/**/* || :"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -23,6 +24,7 @@
"dependencies": { "dependencies": {
"@types/classnames": "0.0.32", "@types/classnames": "0.0.32",
"@types/node": "^7.0.13", "@types/node": "^7.0.13",
"@types/object-assign": "^4.0.30",
"@types/react": "^15.0.23", "@types/react": "^15.0.23",
"@types/react-dom": "^15.5.0", "@types/react-dom": "^15.5.0",
"@types/react-fontawesome": "^1.5.0", "@types/react-fontawesome": "^1.5.0",
@ -30,12 +32,13 @@
"font-awesome": "^4.7.0", "font-awesome": "^4.7.0",
"mobx": "^3.1.9", "mobx": "^3.1.9",
"mobx-react": "^4.1.8", "mobx-react": "^4.1.8",
"object-assign": "^4.1.1",
"paho-mqtt": "^1.0.3", "paho-mqtt": "^1.0.3",
"react": "^15.5.4", "react": "^15.5.4",
"react-dom": "^15.5.4", "react-dom": "^15.5.4",
"react-fontawesome": "^1.6.1", "react-fontawesome": "^1.6.1",
"semantic-ui-css": "^2.2.10", "semantic-ui-css": "^2.2.10",
"semantic-ui-react": "^0.67.0" "semantic-ui-react": "^0.67.2"
}, },
"devDependencies": { "devDependencies": {
"@types/webpack-env": "^1.13.0", "@types/webpack-env": "^1.13.0",

1
tsconfig.json

@ -8,6 +8,7 @@
}, },
"files": [ "files": [
"./node_modules/@types/webpack-env/index.d.ts", "./node_modules/@types/webpack-env/index.d.ts",
"./app/script/paho-mqtt.d.ts",
"./app/script/index.tsx" "./app/script/index.tsx"
] ]
} }

25
tslint.json

@ -0,0 +1,25 @@
{
"defaultSeverity": "error",
"extends": [
"tslint:recommended"
],
"jsRules": {},
"rules": {
"no-console": [
false
],
"max-classes-per-file": [
false
],
"ordered-imports": [
false
],
"variable-name": [
"allow-leading-underscore"
],
"no-namespace": [
"allow-declarations"
]
},
"rulesDirectory": []
}
Loading…
Cancel
Save