diff --git a/.eslintrc b/.eslintrc index 5fb449d3..5e65d39e 100644 --- a/.eslintrc +++ b/.eslintrc @@ -7,7 +7,7 @@ "extends": [ "standard", "plugin:@typescript-eslint/recommended", - "plugin:vue/recommended" + "plugin:vue/vue3-recommended" ], "parser": "vue-eslint-parser", "parserOptions": { @@ -17,6 +17,7 @@ "requireConfigFile": false }, "plugins": [ + "vue", "@typescript-eslint" ], "rules": { diff --git a/.gitignore b/.gitignore index 6294b57c..51c32e82 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ .DS_Store dist build +misc/* +!misc/.gitkeep node_modules thumbs.db NOTES.md diff --git a/.vscode/launch.json b/.vscode/launch.json index 3de1b3cf..a6855376 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -28,7 +28,7 @@ "sourceMaps": true, "type": "node", "timeout": 1000000 - }, + } ], "compounds": [ { diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7fd4e9d4..a103125d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ Antares SQL is an application based on [Electron.js](https://www.electronjs.org/) that uses [Vue.js](https://vuejs.org/) and [Spectre.css](https://picturepan2.github.io/spectre/) as frontend frameworks. For the build process it takes advantage of [electron-builder](https://www.electron.build/). -This application uses [Vuex](https://vuex.vuejs.org/) as application state manager and [electron-store](https://github.com/sindresorhus/electron-store) to save the various settings on disc. +This application uses [Pinia🍍](https://pinia.vuejs.org/) as application state manager and [electron-store](https://github.com/sindresorhus/electron-store) to save the various settings on disc. This guide aims to provide useful information and guidelines to everyone wants to contribute with this open-source project. For every other question related to this project please [contact me](https://github.com/Fabio286). @@ -14,7 +14,7 @@ The main files of the application are located inside `src` folder and are groupp This folder contains small libraries, classes and objects. The purpose of `common` folder is to group together utilities used by **renderer** and **main** processes. Noteworthy is the `customizations` folder that contains clients related customizations. Those settings are merged with `default.js` that lists every option. -Client related customizations are stored on Vuex and can be accessed by `customizations` property of current workspace object, or importing `common/customizations`. +Client related customizations are stored on Pinia and can be accessed by `customizations` property of current workspace object, or importing `common/customizations`. An use case of customizations object can be the following: @@ -62,12 +62,6 @@ The command to build Antares SQL locally is `npm run build:local`. - [Order of words in component names](https://vuejs.org/v2/style-guide/#Order-of-words-in-component-names-strongly-recommended). - **kebab-case** in templates for property and event names. -### Vuex - -- **snake_case** for state names. -- **camelCase** for getter and action names. -- **SNAKE_CASE (all caps)** for mutation names. - ### Code Style The project includes [ESlint](https://eslint.org/) and [StyleLint](https://stylelint.io/) config files with style rules. I recommend to set the lint on-save option in your code editor. diff --git a/misc/.gitkeep b/misc/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/package.json b/package.json index 944d32e4..98c94f45 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,10 @@ "rebuild:electron": "rimraf ./dist && npm run postinstall", "release": "standard-version", "release:pre": "npm run release -- --prerelease alpha", - "postinstall": "electron-builder install-app-deps", - "test": "npm run compile && node tests/app.spec.js", + "devtools:install": "node scripts/devtoolsInstaller", + "postinstall": "electron-builder install-app-deps && npm run devtools:install", + "test": "npm run compile && npm run test:dry", + "test:dry": "xvfb-maybe -- playwright test", "lint": "eslint . --ext .js,.vue && stylelint \"./src/**/*.{css,scss,sass,vue}\"", "lint:fix": "eslint . --ext .js,.vue --fix && stylelint \"./src/**/*.{css,scss,sass,vue}\" --fix", "contributors:add": "all-contributors add", @@ -27,6 +29,9 @@ }, "author": "Fabio Di Stasio ", "main": "./dist/main.js", + "antares": { + "devtoolsId": "nhdogjmejiglipccpnnnanhbledajbpd" + }, "build": { "appId": "com.fabio286.antares", "artifactName": "${productName}-${version}-${os}_${arch}.${ext}", @@ -115,7 +120,7 @@ "electron-store": "^8.0.1", "electron-updater": "^4.6.1", "electron-window-state": "^5.0.3", - "faker": "^5.5.3", + "encoding": "^0.1.13", "leaflet": "^1.7.1", "marked": "^4.0.0", "moment": "^2.29.1", @@ -123,24 +128,27 @@ "pg": "^8.7.1", "pg-query-stream": "^4.2.3", "pgsql-ast-parser": "^7.2.1", + "pinia": "^2.0.13", "source-map-support": "^0.5.20", "spectre.css": "^0.5.9", "sql-formatter": "^4.0.2", "ssh2-promise": "^1.0.2", "v-mask": "^2.3.0", - "vue-i18n": "^8.26.5", - "vuedraggable": "^2.24.3", - "vuex": "^3.6.2" + "vue": "^3.2.33", + "vue-i18n": "^9.1.9", + "vuedraggable": "^4.1.0" }, "devDependencies": { "@babel/eslint-parser": "^7.15.7", "@babel/preset-env": "^7.15.8", "@babel/preset-typescript": "^7.16.7", + "@playwright/test": "^1.21.1", "@types/better-sqlite3": "^7.5.0", "@types/node": "^17.0.23", "@types/pg": "^8.6.5", "@typescript-eslint/eslint-plugin": "^5.18.0", "@typescript-eslint/parser": "^5.18.0", + "@vue/compiler-sfc": "^3.2.33", "all-contributors-cli": "^6.20.0", "babel-loader": "^8.2.3", "chalk": "^4.1.2", @@ -148,7 +156,6 @@ "css-loader": "^6.5.0", "electron": "^17.0.1", "electron-builder": "^22.14.11", - "electron-devtools-installer": "^3.2.0", "eslint": "^7.32.0", "eslint-config-standard": "^16.0.3", "eslint-plugin-import": "^2.24.2", @@ -159,7 +166,8 @@ "html-webpack-plugin": "^5.5.0", "mini-css-extract-plugin": "~2.4.5", "node-loader": "^2.0.0", - "playwright": "^1.18.1", + "playwright": "^1.21.1", + "playwright-core": "^1.21.1", "progress-webpack-plugin": "^1.0.12", "rimraf": "^3.0.2", "sass": "^1.42.1", @@ -172,12 +180,12 @@ "tree-kill": "^1.2.2", "ts-loader": "^9.2.8", "typescript": "^4.6.3", - "vue": "^2.6.14", + "unzip-crx-3": "^0.2.0", "vue-eslint-parser": "^8.3.0", - "vue-loader": "^15.9.8", - "vue-template-compiler": "^2.6.14", + "vue-loader": "^16.8.3", "webpack": "^5.60.0", "webpack-cli": "^4.9.1", - "webpack-dev-server": "^4.4.0" + "webpack-dev-server": "^4.4.0", + "xvfb-maybe": "^0.2.1" } } diff --git a/scripts/devRunner.js b/scripts/devRunner.js index f50f8d6a..a981080c 100644 --- a/scripts/devRunner.js +++ b/scripts/devRunner.js @@ -1,5 +1,6 @@ process.env.NODE_ENV = 'development'; // process.env.ELECTRON_ENABLE_LOGGING = true +process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = false; const chalk = require('chalk'); const electron = require('electron'); @@ -19,7 +20,7 @@ let manualRestart = null; const remoteDebugging = process.argv.includes('--remote-debug'); if (remoteDebugging) { - // disable dvtools open in electron + // disable devtools open in electron process.env.RENDERER_REMOTE_DEBUGGING = true; } diff --git a/scripts/devtoolsInstaller.js b/scripts/devtoolsInstaller.js new file mode 100644 index 00000000..16b6bb5b --- /dev/null +++ b/scripts/devtoolsInstaller.js @@ -0,0 +1,48 @@ +// @ts-check +const fs = require('fs'); +const path = require('path'); +const https = require('https'); +const unzip = require('unzip-crx-3'); +const { antares } = require('../package.json'); + +const extensionID = antares.devtoolsId; +const destFolder = path.resolve(__dirname, `../misc/${extensionID}`); +const filePath = path.resolve(__dirname, `${destFolder}${extensionID}.crx`); +const fileUrl = `https://clients2.google.com/service/update2/crx?response=redirect&acceptformat=crx2,crx3&x=id%3D${extensionID}%26uc&prodversion=32`; +const fileStream = fs.createWriteStream(filePath); + +const downloadFile = url => { + return new Promise((resolve, reject) => { + const request = https.get(url); + + request.on('response', response => { + if (response.statusCode && response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) { + return downloadFile(response.headers.location) + .then(resolve) + .catch(reject); + } + + response.pipe(fileStream); + + response.on('close', () => { + console.log('Devtools download completed!'); + resolve(); + }); + response.on('error', reject); + }); + request.on('error', reject); + request.end(); + }); +}; + +(async () => { + try { + await downloadFile(fileUrl); + await unzip(filePath, destFolder); + fs.unlinkSync(filePath); + fs.unlinkSync(`${destFolder}/package.json`);// <- Avoid to display annoyng npm script in vscode + } + catch (error) { + console.log(error); + } +})(); diff --git a/src/common/libs/mimeFromHex.js b/src/common/libs/mimeFromHex.js index 837b127e..13613acf 100644 --- a/src/common/libs/mimeFromHex.js +++ b/src/common/libs/mimeFromHex.js @@ -43,4 +43,4 @@ export function mimeFromHex (hex) { } } } -}; +} diff --git a/src/common/libs/uidGen.js b/src/common/libs/uidGen.js index f8def6b1..929fc648 100644 --- a/src/common/libs/uidGen.js +++ b/src/common/libs/uidGen.js @@ -5,4 +5,4 @@ */ export function uidGen (prefix) { return (prefix ? `${prefix}:` : '') + Math.random().toString(36).substr(2, 9).toUpperCase(); -}; +} diff --git a/src/main/ipc-handlers/application.ts b/src/main/ipc-handlers/application.ts index a8033a2d..f9c17654 100644 --- a/src/main/ipc-handlers/application.ts +++ b/src/main/ipc-handlers/application.ts @@ -10,7 +10,7 @@ export default () => { event.returnValue = key; }); - ipcMain.handle('showOpenDialog', (event, options) => { + ipcMain.handle('show-open-dialog', (event, options) => { return dialog.showOpenDialog(options); }); diff --git a/src/main/ipc-handlers/connection.ts b/src/main/ipc-handlers/connection.ts index 50f406ba..36462f3a 100644 --- a/src/main/ipc-handlers/connection.ts +++ b/src/main/ipc-handlers/connection.ts @@ -135,9 +135,6 @@ export default (connections: {[key: string]: antares.Client}) => { await connection.connect(); - // TODO: temporary - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore const structure = await connection.getStructure(new Set()); connections[conn.uid] = connection; diff --git a/src/main/main.ts b/src/main/main.ts index 563b66c6..4c3137dc 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -47,22 +47,8 @@ async function createMainWindow () { remoteMain.enable(window.webContents); try { - if (isDevelopment) { - const { default: installExtension, VUEJS3_DEVTOOLS } = require('electron-devtools-installer'); - const options = { - loadExtensionOptions: { allowFileAccess: true } - }; - - try { - const name = await installExtension(VUEJS3_DEVTOOLS, options); - console.log(`Added Extension: ${name}`); - } - catch (err) { - console.log('An error occurred: ', err); - } - + if (isDevelopment) await window.loadURL('http://localhost:9080'); - } else { const indexPath = path.resolve(__dirname, 'index.html'); await window.loadFile(indexPath); @@ -109,8 +95,8 @@ else { mainWindow = await createMainWindow(); createAppMenu(); - // if (isDevelopment) - // mainWindow.webContents.openDevTools(); + if (isDevelopment) + mainWindow.webContents.openDevTools(); process.on('uncaughtException', error => { mainWindow.webContents.send('unhandled-exception', error); @@ -120,6 +106,14 @@ else { mainWindow.webContents.send('unhandled-exception', error); }); }); + + app.on('browser-window-created', (event, window) => { + if (isDevelopment) { + const { antares } = require('../../package.json'); + const extensionPath = path.resolve(__dirname, `../../misc/${antares.devtoolsId}`); + window.webContents.session.loadExtension(extensionPath, { allowFileAccess: true }).catch(console.error); + } + }); } function createAppMenu () { diff --git a/src/renderer/App.vue b/src/renderer/App.vue index 698e6541..eae823ec 100644 --- a/src/renderer/App.vue +++ b/src/renderer/App.vue @@ -25,37 +25,56 @@ diff --git a/src/renderer/components/BaseConfirmModal.vue b/src/renderer/components/BaseConfirmModal.vue index e562d9fa..2339be89 100644 --- a/src/renderer/components/BaseConfirmModal.vue +++ b/src/renderer/components/BaseConfirmModal.vue @@ -1,44 +1,48 @@ @@ -58,6 +62,7 @@ export default { confirmText: String, cancelText: String }, + emits: ['confirm', 'hide'], computed: { hasHeader () { return !!this.$slots.header; @@ -81,7 +86,7 @@ export default { created () { window.addEventListener('keydown', this.onKey); }, - beforeDestroy () { + beforeUnmount () { window.removeEventListener('keydown', this.onKey); }, methods: { diff --git a/src/renderer/components/BaseContextMenu.vue b/src/renderer/components/BaseContextMenu.vue index 9be75aed..feee059e 100644 --- a/src/renderer/components/BaseContextMenu.vue +++ b/src/renderer/components/BaseContextMenu.vue @@ -21,6 +21,7 @@ export default { props: { contextEvent: MouseEvent }, + emits: ['close-context'], data () { return { contextSize: null, @@ -61,7 +62,7 @@ export default { if (this.$refs.contextContent) this.contextSize = this.$refs.contextContent.getBoundingClientRect(); }, - beforeDestroy () { + beforeUnmount () { window.removeEventListener('keydown', this.onKey); }, methods: { diff --git a/src/renderer/components/BaseNotification.vue b/src/renderer/components/BaseNotification.vue index b8cbf8f8..cd769186 100644 --- a/src/renderer/components/BaseNotification.vue +++ b/src/renderer/components/BaseNotification.vue @@ -27,6 +27,7 @@ export default { default: '' } }, + emits: ['close'], data () { return { isExpanded: false diff --git a/src/renderer/components/BaseTextEditor.vue b/src/renderer/components/BaseTextEditor.vue index f8083654..a2eb4c67 100644 --- a/src/renderer/components/BaseTextEditor.vue +++ b/src/renderer/components/BaseTextEditor.vue @@ -11,13 +11,15 @@ diff --git a/src/renderer/components/WorkspaceExploreBar.vue b/src/renderer/components/WorkspaceExploreBar.vue index 856e69f8..3ebb25d5 100644 --- a/src/renderer/components/WorkspaceExploreBar.vue +++ b/src/renderer/components/WorkspaceExploreBar.vue @@ -115,7 +115,12 @@ diff --git a/src/renderer/components/WorkspaceTabNewTrigger.vue b/src/renderer/components/WorkspaceTabNewTrigger.vue index ae8bb1ae..97600f9f 100644 --- a/src/renderer/components/WorkspaceTabNewTrigger.vue +++ b/src/renderer/components/WorkspaceTabNewTrigger.vue @@ -122,7 +122,7 @@