diff --git a/.babelrc b/.babelrc index 9143cd8d1..a64b362c2 100644 --- a/.babelrc +++ b/.babelrc @@ -3,6 +3,6 @@ [ "env", {"modules": false} ], "stage-2" ], - "plugins": ["transform-runtime"], + "plugins": ["transform-runtime", "transform-decorators"], "comments": false } diff --git a/.eslintignore b/.eslintignore index 9b1c8b133..25aeecd2a 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,2 @@ /dist +*.ts diff --git a/package-lock.json b/package-lock.json index b63cda78a..c2540a90a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2771,6 +2771,12 @@ "integrity": "sha1-ogM8CcyOFY03dI+951B4Mr1s4Sc=", "dev": true }, + "diff": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.4.0.tgz", + "integrity": "sha512-QpVuMTEoJMF7cKzi6bvWhRulU1fZqZnvyVQgNhPaxxuTYwyjn/j1v9falseQ/uXWwPnO56RBfwtg4h/EQXmucA==", + "dev": true + }, "diffie-hellman": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", @@ -10292,6 +10298,112 @@ "semver": "5.5.0" } }, + "tslib": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz", + "integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==", + "dev": true + }, + "tslint": { + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.9.1.tgz", + "integrity": "sha1-ElX4ej/1frCw4fDmEKi0dIBGya4=", + "dev": true, + "requires": { + "babel-code-frame": "6.26.0", + "builtin-modules": "1.1.1", + "chalk": "2.3.1", + "commander": "2.14.1", + "diff": "3.4.0", + "glob": "7.1.2", + "js-yaml": "3.10.0", + "minimatch": "3.0.4", + "resolve": "1.5.0", + "semver": "5.5.0", + "tslib": "1.9.0", + "tsutils": "2.21.2" + } + }, + "tslint-config-airbnb": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/tslint-config-airbnb/-/tslint-config-airbnb-5.7.0.tgz", + "integrity": "sha1-Cf8EsN1Zl2X0S0QlAERY0I/LLEA=", + "dev": true, + "requires": { + "tslint-consistent-codestyle": "1.11.1", + "tslint-eslint-rules": "4.1.1", + "tslint-microsoft-contrib": "5.0.3" + } + }, + "tslint-consistent-codestyle": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/tslint-consistent-codestyle/-/tslint-consistent-codestyle-1.11.1.tgz", + "integrity": "sha512-wLu+Ct8x4mBmVkuhEiNAnUBkxchMV2Le0ikBsST5HnKbGlm3K4RSpXCBSI1VtJDk748W2I5hDzgsInawLdnxwQ==", + "dev": true, + "requires": { + "tslib": "1.9.0", + "tsutils": "2.21.2" + } + }, + "tslint-eslint-rules": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tslint-eslint-rules/-/tslint-eslint-rules-4.1.1.tgz", + "integrity": "sha1-fDDniC8mvCdr/5HSOEl1xp2viLo=", + "dev": true, + "requires": { + "doctrine": "0.7.2", + "tslib": "1.9.0", + "tsutils": "1.9.1" + }, + "dependencies": { + "doctrine": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-0.7.2.tgz", + "integrity": "sha1-fLhgNZujvpDgQLJrcpzkv6ZUxSM=", + "dev": true, + "requires": { + "esutils": "1.1.6", + "isarray": "0.0.1" + } + }, + "esutils": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.1.6.tgz", + "integrity": "sha1-wBzKqa5LiXxtDD4hCuUvPHqEQ3U=", + "dev": true + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "tsutils": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-1.9.1.tgz", + "integrity": "sha1-ufmrROVa+WgYMdXyjQrur1x1DLA=", + "dev": true + } + } + }, + "tslint-microsoft-contrib": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/tslint-microsoft-contrib/-/tslint-microsoft-contrib-5.0.3.tgz", + "integrity": "sha512-5AnfTGlfpUzpRHLmoojPBKFTTmbjnwgdaTHMdllausa4GBPya5u36i9ddrTX4PhetGZvd4JUYIpAmgHqVnsctg==", + "dev": true, + "requires": { + "tsutils": "2.21.2" + } + }, + "tsutils": { + "version": "2.21.2", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.21.2.tgz", + "integrity": "sha512-iaIuyjIUeFLdD39MYdzqBuY7Zv6+uGxSwRH4mf+HuzsnznjFz0R2tGrAe0/JvtNh91WrN8UN/DZRFTZNDuVekA==", + "dev": true, + "requires": { + "tslib": "1.9.0" + } + }, "tty-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", @@ -10854,6 +10966,11 @@ "integrity": "sha512-3D+lY7HTkKbtswDM4BBHgqyq+qo8IAEE8lz8va1dz3LLmttjgo0FxairO4r1iN2OBqk8o1FyL4hvzzTFEdQSEw==", "dev": true }, + "vue-class-component": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/vue-class-component/-/vue-class-component-6.2.0.tgz", + "integrity": "sha512-U11yVeP5zjPSx4IU7Zas3MLC+Vy9dmufI+uLKLo8YuGQJGOihSYfh/fgNnbjMteN+hz5axjG6iC6ybMo6vGYnA==" + }, "vue-eslint-parser": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-2.0.2.tgz", diff --git a/package.json b/package.json index 1cbe71041..0ac272d54 100644 --- a/package.json +++ b/package.json @@ -7,16 +7,20 @@ "author": "BrewPi B.V.", "private": true, "scripts": { - "lint": "eslint --ext .js,.vue,.ts src", + "eslint": "eslint --ext .js,.vue src", + "tslint": "tslint -c tslint.json 'src/**/*.ts'", + "lint": "npm run eslint && npm run tslint", "test": "echo \"No test specified\" && exit 0" }, "dependencies": { + "vue-class-component": "^6.2.0", "vue-i18n": "^7.3.3", "vuex-typescript": "^3.0.2" }, "devDependencies": { "@types/core-js": "^0.9.46", "babel-eslint": "8.2.1", + "babel-plugin-transform-decorators": "^6.24.1", "connect-api-mocker": "^1.3.6", "eslint": "4.15.0", "eslint-config-airbnb-base": "11.3.0", @@ -27,6 +31,8 @@ "eslint-plugin-vue": "4.0.0", "quasar-cli": "^0.15.0-beta.42", "ts-loader": "^3.5.0", + "tslint": "^5.9.1", + "tslint-config-airbnb": "^5.7.0", "typescript": "^2.7.1", "typescript-eslint-parser": "^13.0.0" }, diff --git a/quasar.conf.js b/quasar.conf.js index 022dba303..c5b769fd3 100644 --- a/quasar.conf.js +++ b/quasar.conf.js @@ -36,17 +36,17 @@ module.exports = ctx => ({ // add custom loaders cfg.module.rules.push({ - enforce: 'pre', - test: /\.(js|vue)$/, - loader: 'eslint-loader', - exclude: /(node_modules|quasar)/, - }, { test: /\.tsx?$/, loader: 'ts-loader', exclude: /node_modules/, options: { appendTsSuffixTo: [/\.vue$/], }, + }, { + enforce: 'pre', + test: /\.(js|vue)$/, + loader: 'eslint-loader', + exclude: /(node_modules|quasar)/, }); }, }, diff --git a/src/components/blocks/OneWireTempSensor/OneWireTempSensor.ts b/src/components/blocks/OneWireTempSensor/OneWireTempSensor.ts new file mode 100644 index 000000000..4e6c8ff1e --- /dev/null +++ b/src/components/blocks/OneWireTempSensor/OneWireTempSensor.ts @@ -0,0 +1,26 @@ +import Vue from 'vue'; +import Component from 'vue-class-component'; + +import { getById } from '../../../store/blocks/OneWireTempSensor/getters'; + +@Component({ + props: { + id: { + default: '', + type: String, + }, + }, +}) +export default class OneWireTempSensor extends Vue { + get blockData() { + return getById(this.$props.id); + } + + get settings() { + return this.blockData.settings; + } + + get state() { + return this.blockData.state; + } +} diff --git a/src/components/blocks/OneWireTempSensor.vue b/src/components/blocks/OneWireTempSensor/default.vue similarity index 72% rename from src/components/blocks/OneWireTempSensor.vue rename to src/components/blocks/OneWireTempSensor/default.vue index e85ca8218..373209bd9 100644 --- a/src/components/blocks/OneWireTempSensor.vue +++ b/src/components/blocks/OneWireTempSensor/default.vue @@ -26,24 +26,7 @@ - + - - + diff --git a/src/components/blocks/block.ts b/src/components/blocks/block.ts deleted file mode 100644 index b6a920332..000000000 --- a/src/components/blocks/block.ts +++ /dev/null @@ -1,24 +0,0 @@ -import Vue from 'vue'; - -import SetPointSimple from './SetPointSimple.vue'; -import OneWireTempSensor from './OneWireTempSensor.vue'; - -export default Vue.extend({ - name: 'block', - props: ['block-data'], - render(createElement) { - const { type } = this.$props.blockData; - const options = { - props: this.$props.blockData, - }; - - switch (type) { - case 'OneWireTempSensor': - return createElement(OneWireTempSensor, options); - case 'SetPointSimple': - return createElement(SetPointSimple, options); - default: - throw new Error(`'${type}' is not a valid block type`); - } - }, -}); diff --git a/src/components/blocks/block.vue b/src/components/blocks/block.vue new file mode 100644 index 000000000..1ab09242e --- /dev/null +++ b/src/components/blocks/block.vue @@ -0,0 +1,39 @@ + + + diff --git a/src/main.ts b/src/main.ts index 59d692c5b..229a718bd 100644 --- a/src/main.ts +++ b/src/main.ts @@ -71,9 +71,9 @@ Vue.use(Quasar, { }); const app = new Vue({ - el: '#q-app', router, store, + el: '#q-app', render: h => h(App), }); diff --git a/src/pages/blocks.vue b/src/pages/blocks.vue index f76f3cf02..d10a5f7ff 100644 --- a/src/pages/blocks.vue +++ b/src/pages/blocks.vue @@ -10,8 +10,8 @@ @@ -25,13 +25,13 @@ import Vue from 'vue'; import Block from '../components/blocks/block'; -import { isFetching, allBlocks } from '../store/blocks/getters'; +import { isFetching, blockIds } from '../store/blocks/getters'; export default Vue.extend({ name: 'PageIndex', components: { Block }, computed: { - blocks: () => allBlocks(), + blocks: () => blockIds(), fetching: () => isFetching(), }, methods: {}, diff --git a/src/plugins/i18n.ts b/src/plugins/i18n.ts index cffd90207..c75a9afee 100644 --- a/src/plugins/i18n.ts +++ b/src/plugins/i18n.ts @@ -7,8 +7,8 @@ export default ({ app, store, Vue }: PluginArguments) => { // Set i18n instance on app // This way we can use it in middleware and pages asyncData/fetch app.i18n = new VueI18n({ + messages, locale: store.state.locale, fallbackLocale: 'en', - messages, }); }; diff --git a/src/router/index.ts b/src/router/index.ts index 4f18272ff..8a7433b3f 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -5,11 +5,11 @@ import routes from './routes'; Vue.use(VueRouter); -const Router = new VueRouter({ +const router = new VueRouter({ + routes, // Leave as is and change from quasar.conf.js instead! mode: process.env.VUE_ROUTER_MODE || 'history', base: process.env.VUE_ROUTER_BASE || '', - routes, }); /* @@ -23,4 +23,4 @@ Router.beforeEach((to, from, next) => { }) */ -export default Router; +export default router; diff --git a/src/store/blocks/OneWireTempSensor/OneWireTempSensor.d.ts b/src/store/blocks/OneWireTempSensor/OneWireTempSensor.d.ts index db26f0870..d52b64016 100644 --- a/src/store/blocks/OneWireTempSensor/OneWireTempSensor.d.ts +++ b/src/store/blocks/OneWireTempSensor/OneWireTempSensor.d.ts @@ -4,11 +4,11 @@ export interface OneWireTempSensor extends BlockBase { settings: { address: string, offset: number, - }, + }; state: { value: number, connected: boolean, - }, + }; } export interface OneWireTempSensorBlock extends OneWireTempSensor { diff --git a/src/store/blocks/OneWireTempSensor/getters.ts b/src/store/blocks/OneWireTempSensor/getters.ts new file mode 100644 index 000000000..cc010d7a9 --- /dev/null +++ b/src/store/blocks/OneWireTempSensor/getters.ts @@ -0,0 +1,14 @@ +import { blockById } from '../getters'; + +import { OneWireTempSensorBlock } from './OneWireTempSensor'; + +export function getById(id: string): OneWireTempSensorBlock { + const block = blockById(id); + + // force block type + if (block.type !== 'OneWireTempSensor') { + throw new Error('Block is not a valid OneWireTempSensor'); + } + + return block; +} diff --git a/src/store/blocks/SetPointSimple/SetPointSimple.d.ts b/src/store/blocks/SetPointSimple/SetPointSimple.d.ts index 0137c25c3..2009036c1 100644 --- a/src/store/blocks/SetPointSimple/SetPointSimple.d.ts +++ b/src/store/blocks/SetPointSimple/SetPointSimple.d.ts @@ -3,7 +3,7 @@ import { BlockBase } from '../state'; export interface SetPointSimple extends BlockBase { settings: { value: number, - }, + }; } export interface SetPointSimpleBlock extends SetPointSimple { diff --git a/src/store/blocks/SetPointSimple/getters.ts b/src/store/blocks/SetPointSimple/getters.ts new file mode 100644 index 000000000..14724b73a --- /dev/null +++ b/src/store/blocks/SetPointSimple/getters.ts @@ -0,0 +1,14 @@ +import { blockById } from '../getters'; + +import { SetPointSimpleBlock } from './SetPointSimple'; + +export function getById(id: string): SetPointSimpleBlock { + const block = blockById(id); + + // force block type + if (block.type !== 'SetPointSimple') { + throw new Error('Block is not a valid SetPointSimple'); + } + + return block; +} diff --git a/src/store/blocks/getters.ts b/src/store/blocks/getters.ts index b1e042c60..e1578861b 100644 --- a/src/store/blocks/getters.ts +++ b/src/store/blocks/getters.ts @@ -7,6 +7,10 @@ import { State as RootState } from '../state'; const { read } = getStoreAccessors('blocks'); const getters = { + blocksById: (state: BlocksState): { [id: string]: Block } => state.byId, + blockIds(state: BlocksState): string[] { + return state.allIds; + }, allBlocks(state: BlocksState): Block[] { return state.allIds.map(id => state.byId[id]); }, @@ -17,8 +21,12 @@ const getters = { const readIsFetching = read(getters.isFetching); const readAllBlocks = read(getters.allBlocks); +const readBlockIds = read(getters.blockIds); +const readBlocksById = read(getters.blocksById); export const allBlocks = () => readAllBlocks(store); +export const blockIds = () => readBlockIds(store); +export const blockById = (id: string) => readBlocksById(store)[id]; export const isFetching = () => readIsFetching(store); export default getters; diff --git a/src/store/blocks/index.ts b/src/store/blocks/index.ts index dbaf894c2..a4e90507b 100644 --- a/src/store/blocks/index.ts +++ b/src/store/blocks/index.ts @@ -3,6 +3,9 @@ import getters from './getters'; import mutations from './mutations'; const blocks = { + getters, + actions, + mutations, namespaced: true, strict: true, state: { @@ -10,9 +13,6 @@ const blocks = { byId: {}, fetching: false, }, - getters, - actions, - mutations, }; export default blocks; diff --git a/src/store/blocks/state.ts b/src/store/blocks/state.ts index 1743219ec..8ad5e3876 100644 --- a/src/store/blocks/state.ts +++ b/src/store/blocks/state.ts @@ -5,7 +5,7 @@ import { OneWireTempSensorBlock, OneWireTempSensor } from './OneWireTempSensor/O import { State as RootState } from '../state'; export interface BlockBase { - id: string, + id: string; } export type Block = SetPointSimpleBlock | OneWireTempSensorBlock; diff --git a/src/vue-shims.d.ts b/src/vue-shims.d.ts index 9bb374711..05ee2ac80 100644 --- a/src/vue-shims.d.ts +++ b/src/vue-shims.d.ts @@ -1,9 +1,9 @@ // standard declarations for *.vue files declare module '*.vue' { - import Vue from 'vue'; // eslint-disable-line + import Vue from 'vue'; export default Vue; } // Quasar specific declarations declare module 'quasar'; -declare const __THEME: string; // eslint-disable-line +declare const __THEME: string; diff --git a/tsconfig.json b/tsconfig.json index bb7d9b318..e4f991efe 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,6 +4,7 @@ "strict": true, "lib": ["ESNext", "DOM"], "moduleResolution": "node", - "sourceMap": true + "sourceMap": true, + "experimentalDecorators": true } } diff --git a/tslint.json b/tslint.json new file mode 100644 index 000000000..740a3e241 --- /dev/null +++ b/tslint.json @@ -0,0 +1,12 @@ +{ + "defaultSeverity": "error", + "extends": [ + "tslint-config-airbnb" + ], + "jsRules": {}, + "rules": { + "import-name": false, + "no-boolean-literal-compare": false + }, + "rulesDirectory": [] +}