diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index e3f3e76a6..000000000 --- a/.eslintrc.js +++ /dev/null @@ -1,131 +0,0 @@ -module.exports = { - root: true, - env: { - browser: true, - }, - extends: [ - 'plugin:vue/recommended', - '@vue/standard', - '@vue/typescript/recommended', - ], - parserOptions: { - ecmaVersion: 2020, - }, - globals: { - bridge: true, - chrome: true, - localStorage: 'off', - HTMLDocument: true, - name: 'off', - browser: true, - }, - rules: { - 'vue/html-closing-bracket-newline': [ - 'error', - { - singleline: 'never', - multiline: 'always', - }, - ], - 'no-var': ['error'], - '@typescript-eslint/member-delimiter-style': [ - 'error', - { - multiline: { - delimiter: 'none', - }, - singleline: { - delimiter: 'comma', - }, - }, - ], - 'func-call-spacing': 'off', - 'vue/component-definition-name-casing': 'off', - 'vue/multi-word-component-names': 'off', - '@typescript-eslint/ban-ts-comment': 'warn', - '@typescript-eslint/no-use-before-define': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', - camelcase: 'warn', - 'no-prototype-builtins': 'off', - 'no-use-before-define': 'off', - 'no-console': ['error', { allow: ['warn', 'error'] }], - 'comma-dangle': ['error', 'always-multiline'], - quotes: ['error', 'single', { allowTemplateLiterals: true }], - }, - ignorePatterns: [ - 'node_modules/', - '/packages/*/lib/', - 'dist/', - 'build/', - 'build-node/', - '/legacy', - ], - overrides: [ - { - files: [ - 'release.js', - 'sign-firefox.js', - 'extension-zips.js', - 'packages/build-tools/**', - 'packages/shell-electron/**', - '**webpack.config.js', - ], - rules: { - '@typescript-eslint/no-var-requires': 'off', - '@typescript-eslint/camelcase': 'off', - }, - }, - { - files: ['packages/shell-dev-vue3/**'], - rules: { - 'vue/valid-template-root': 'off', - 'vue/one-component-per-file': 'off', - 'vue/no-v-model-argument': 'off', - }, - }, - { - files: ['packages/app-frontend/**'], - rules: { - 'vue/no-v-model-argument': 'off', - }, - }, - { - files: [ - 'packages/shell-dev-vue2/**', - 'packages/shell-dev-vue3/**', - ], - rules: { - '@typescript-eslint/no-unused-vars': 'off', - 'vue/require-default-prop': 'off', - 'vue/require-prop-types': 'off', - 'no-console': 'off', - }, - }, - { - files: [ - 'packages/shell-host/**', - ], - globals: { - localStorage: false, - }, - rules: { - 'no-console': 'off', - 'vue/no-multiple-template-root': 'off', - }, - }, - { - files: [ - 'packages/app-backend-core/src/hook.ts', - ], - rules: { - 'no-restricted-syntax': ['error', { - selector: 'ImportDeclaration', - message: 'File is injected with a `Function.toString()`, imports will not work', - }, { - selector: `CallExpression[callee.name='require']`, - message: 'File is injected with a `Function.toString()`, require will not work', - }], - }, - }, - ], -} diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 66c9292d2..ebd154bd1 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,4 +1,4 @@ -name: "\U0001F41E Bug report" +name: 🐞 Bug report description: Create a report to help us improve body: - type: markdown diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 704563f4b..b52c4120f 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,21 +1,21 @@ -name: "\U0001F680 New feature proposal" +name: 🚀 New feature proposal description: Suggest an idea for this project -labels: [":sparkles: feature request"] +labels: [':sparkles: feature request'] body: - type: markdown attributes: value: | - **Before You Start...** + **Before You Start...** - This form is only for submitting feature requests. If you have a usage question - or are unsure if this is really a bug, make sure to: + This form is only for submitting feature requests. If you have a usage question + or are unsure if this is really a bug, make sure to: - - Read the [docs](https://devtools.vuejs.org/) - - Read the [FAQ](https://devtools.vuejs.org/guide/faq.html) - - Ask on [GitHub Discussions](https://github.com/vuejs/devtools/discussions) - - Ask on [Discord Chat](https://chat.vuejs.org/) + - Read the [docs](https://devtools.vuejs.org/) + - Read the [FAQ](https://devtools.vuejs.org/guide/faq.html) + - Ask on [GitHub Discussions](https://github.com/vuejs/devtools/discussions) + - Ask on [Discord Chat](https://chat.vuejs.org/) - Also try to search for your issue - another user may have already requested something similar! + Also try to search for your issue - another user may have already requested something similar! - type: textarea id: problem-description diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index c93564f57..43d3384ef 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -3,7 +3,7 @@ name: Create release on: push: tags: - - "v*" + - 'v*' jobs: build: diff --git a/.vscode/settings.json b/.vscode/settings.json index 3662b3700..25fa6215f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { "typescript.tsdk": "node_modules/typescript/lib" -} \ No newline at end of file +} diff --git a/babel.config.js b/babel.config.js index bc074679d..638ae723f 100644 --- a/babel.config.js +++ b/babel.config.js @@ -2,7 +2,8 @@ module.exports = { root: true, presets: [ [ - '@babel/env', { + '@babel/env', + { modules: false, }, ], diff --git a/cypress/.eslintrc.js b/cypress/.eslintrc.js index 0c938b0ea..4a30a523c 100644 --- a/cypress/.eslintrc.js +++ b/cypress/.eslintrc.js @@ -3,7 +3,7 @@ module.exports = { 'cypress', ], env: { - mocha: true, + 'mocha': true, 'cypress/globals': true, }, rules: { diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json index da18d9352..02e425437 100644 --- a/cypress/fixtures/example.json +++ b/cypress/fixtures/example.json @@ -2,4 +2,4 @@ "name": "Using fixtures to represent data", "email": "hello@cypress.io", "body": "Fixtures are a great way to mock data for responses to routes" -} \ No newline at end of file +} diff --git a/cypress/integration/components-tab.js b/cypress/integration/components-tab.js index b46356bd0..953668254 100644 --- a/cypress/integration/components-tab.js +++ b/cypress/integration/components-tab.js @@ -63,7 +63,7 @@ suite('components tab', () => { cy.get('.data-el.props .data-field:nth-child(2)').contains('msg:"hi"') cy.get('.data-el.props .data-field:nth-child(3)').contains('obj:undefined') // Regexp - cy.get('.data-el.data .data-field:nth-child(8)').then(el => { + cy.get('.data-el.data .data-field:nth-child(8)').then((el) => { expect(el.text()).to.include('regex:/(a\\w+b)/g') }) // Literals @@ -106,7 +106,7 @@ suite('components tab', () => { .click({ force: true }) }) cy.get('.action-header .title').contains('Mine') - cy.get('.tree').then(el => { + cy.get('.tree').then((el) => { expect(el.text()).to.include('') }) }) diff --git a/cypress/integration/vuex-tab.js b/cypress/integration/vuex-tab.js index 3af2f97c1..9ab0214f1 100644 --- a/cypress/integration/vuex-tab.js +++ b/cypress/integration/vuex-tab.js @@ -54,13 +54,13 @@ suite('vuex tab', () => { .should('not.have.class', 'active') cy.get('.recording-vuex-state').should('not.be.visible') cy.get('.loading-vuex-state').should('not.be.visible') - cy.get('.vuex-state-inspector').then(el => { + cy.get('.vuex-state-inspector').then((el) => { expect(el.text()).to.include('type:"INCREMENT"') expect(el.text()).to.include('count:2') expect(el.text()).to.include('Error from getter') }) cy.get('.data-field .key').contains('lastCountPayload').click() - cy.get('.vuex-state-inspector').then(el => { + cy.get('.vuex-state-inspector').then((el) => { expect(el.text()).to.include('a:1') expect(el.text()).to.include('b:Object') }) @@ -88,7 +88,7 @@ suite('vuex tab', () => { cy.get('.recording-vuex-state').should('not.be.visible') cy.get('.loading-vuex-state').should('not.be.visible') cy.get('.recording-vuex-state').should('not.be.visible') - cy.get('.vuex-state-inspector').then(el => { + cy.get('.vuex-state-inspector').then((el) => { expect(el.text()).to.include('type:"INCREMENT"') expect(el.text()).to.include('count:1') }) @@ -111,7 +111,7 @@ suite('vuex tab', () => { cy.get('.history .entry[data-index="0"]') .should('have.class', 'inspected') .should('not.have.class', 'active') - cy.get('.vuex-state-inspector').then(el => { + cy.get('.vuex-state-inspector').then((el) => { expect(el.text()).to.include('count:0') }) cy.get('#target').iframe().then(({ get }) => { @@ -133,7 +133,7 @@ suite('vuex tab', () => { cy.get('.history .entry[data-index="4"]') .should('have.class', 'inspected') .should('have.class', 'active') - cy.get('.vuex-state-inspector').then(el => { + cy.get('.vuex-state-inspector').then((el) => { expect(el.text()).to.include('count:2') }) cy.get('#target').iframe().then(({ get }) => { @@ -147,7 +147,7 @@ suite('vuex tab', () => { cy.get('.history .entry[data-index="0"]') .should('have.class', 'inspected') .should('have.class', 'active') - cy.get('.vuex-state-inspector').then(el => { + cy.get('.vuex-state-inspector').then((el) => { expect(el.text()).to.include('count:2') }) cy.get('#target').iframe().then(({ get }) => { @@ -204,9 +204,9 @@ suite('vuex tab', () => { cy.wait(500) cy.get('.message.invalid-json').should('not.be.visible') cy.wait(500) - cy.get('.vuex-state-inspector').then(el => { + cy.get('.vuex-state-inspector').then((el) => { expect(el.text()).to.include('count:42') - expect(el.text()).to.include('date:' + new Date('Fri Dec 22 2017 10:12:04 GMT+0100 (CET)')) + expect(el.text()).to.include(`date:${new Date('Fri Dec 22 2017 10:12:04 GMT+0100 (CET)')}`) }) cy.get('.import').click() cy.get('.import-state').should('not.be.visible') diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js index 2b174d503..1cb1467bf 100644 --- a/cypress/plugins/index.js +++ b/cypress/plugins/index.js @@ -11,7 +11,7 @@ // This function is called when a project is opened or re-opened (e.g. due to // the project's config changing) -module.exports = (on, config) => { +module.exports = (_on, _config) => { // `on` is used to hook into various events Cypress emits // `config` is the resolved Cypress config diff --git a/cypress/support/commands.js b/cypress/support/commands.js index de6b360e0..336ac74b8 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -25,14 +25,14 @@ // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) Cypress.Commands.add('vueCheckInit', () => { - cy.get('.message .text').should('be.visible', { timeout: 10000 }).then(el => { + cy.get('.message .text').should('be.visible', { timeout: 10000 }).then((el) => { expect(el.text()).to.include('Ready. Detected Vue') }) cy.get('.instance').eq(0).contains('Root') }) // Add iframe support until becomes part of the framework -Cypress.Commands.add('iframe', { prevSubject: 'element' }, $iframe => { +Cypress.Commands.add('iframe', { prevSubject: 'element' }, ($iframe) => { const get = selector => cy.wait(500).wrap($iframe.contents().find(selector)) const el = $iframe[0] @@ -40,7 +40,7 @@ Cypress.Commands.add('iframe', { prevSubject: 'element' }, $iframe => { if (iframeDoc.readyState === 'complete') { return Cypress.Promise.resolve({ body: $iframe.contents().find('body'), get }) } - return new Cypress.Promise(resolve => { + return new Cypress.Promise((resolve) => { $iframe.on('load', () => { resolve({ body: $iframe.contents().find('body'), get }) }) diff --git a/cypress/utils/suite.js b/cypress/utils/suite.js index 184eb4bdb..d1eb6f127 100644 --- a/cypress/utils/suite.js +++ b/cypress/utils/suite.js @@ -1,4 +1,4 @@ -export function suite (description, tests) { +export function suite(description, tests) { describe(description, () => { before(() => { cy.visit('/') diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 000000000..742757cbd --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,34 @@ +const antfu = require('@antfu/eslint-config').default + +module.exports = antfu({ + ignores: [ + '**/dist', + ], +}, { + rules: { + 'curly': ['error', 'all'], + 'node/prefer-global/process': 'off', + }, +}, { + files: [ + 'packages/shell-dev*/**', + ], + rules: { + 'no-console': 'off', + 'unused-imports/no-unused-vars': 'off', + 'vue/require-explicit-emits': 'off', + 'vue/custom-event-name-casing': 'off', + 'vue/no-deprecated-functional-template': 'off', + 'vue/no-deprecated-filter': 'off', + 'vue/no-unused-refs': 'off', + 'vue/require-component-is': 'off', + 'vue/return-in-computed-property': 'off', + }, +}, { + files: [ + 'packages/shell-host/**', + ], + rules: { + 'no-console': 'off', + }, +}) diff --git a/extension-zips.js b/extension-zips.js index 4cc14900c..8a070b7f0 100644 --- a/extension-zips.js +++ b/extension-zips.js @@ -1,7 +1,9 @@ // require modules -const fs = require('fs') -const path = require('path') +const fs = require('node:fs') +const path = require('node:path') +const process = require('node:process') const archiver = require('archiver') + const IS_CI = !!(process.env.CIRCLECI || process.env.GITHUB_ACTIONS) const ProgressBar = !IS_CI ? require('progress') : {} const readDirGlob = !IS_CI ? require('readdir-glob') : {} @@ -18,18 +20,20 @@ const INCLUDE_GLOBS = [ // SKIP_GLOBS makes glob searches more efficient const SKIP_DIR_GLOBS = ['node_modules', 'src'] -function bytesToSize (bytes) { +function bytesToSize(bytes) { const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'] - if (bytes === 0) return '0 Byte' - const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))) - return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i] + if (bytes === 0) { + return '0 Byte' + } + const i = Number.parseInt(Math.floor(Math.log(bytes) / Math.log(1024))) + return `${Math.round(bytes / 1024 ** i, 2)} ${sizes[i]}` } (async () => { await writeZip('devtools-chrome.zip', 'shell-chrome') await writeZip('devtools-firefox.zip', 'shell-chrome') - async function writeZip (fileName, packageDir) { + async function writeZip(fileName, packageDir) { // create a file to stream archive data to. const output = fs.createWriteStream(path.join(__dirname, 'dist', fileName)) const archive = archiver('zip', { @@ -45,14 +49,15 @@ function bytesToSize (bytes) { tSize: '0 Bytes', } - async function parseFileStats () { + async function parseFileStats() { return new Promise((resolve, reject) => { - const globber = readDirGlob(path.join('packages', packageDir), - { pattern: INCLUDE_GLOBS, skip: SKIP_DIR_GLOBS, mark: true, stat: true }) - globber.on('match', match => { - if (!match.stat.isDirectory()) status.total++ + const globber = readDirGlob(path.join('packages', packageDir), { pattern: INCLUDE_GLOBS, skip: SKIP_DIR_GLOBS, mark: true, stat: true }) + globber.on('match', (match) => { + if (!match.stat.isDirectory()) { + status.total++ + } }) - globber.on('error', err => { + globber.on('error', (err) => { reject(err) }) globber.on('end', () => { @@ -60,7 +65,7 @@ function bytesToSize (bytes) { }) }) } - await parseFileStats().catch(err => { + await parseFileStats().catch((err) => { console.error(err) process.exit(1) }) @@ -77,7 +82,7 @@ function bytesToSize (bytes) { const n = entry.name status.written++ status.cFile = n.length > 14 - ? '...' + n.slice(n.length - 11) + ? `...${n.slice(n.length - 11)}` : n status.cSize = bytesToSize(entry.stats.size) status.tBytes += entry.stats.size @@ -101,12 +106,12 @@ function bytesToSize (bytes) { // This event is fired when the data source is drained no matter what was the data source. // It is not part of this library but rather from the NodeJS Stream API. // @see: https://nodejs.org/api/stream.html#stream_event_end - output.on('end', function () { + output.on('end', () => { 'nothing' }) // good practice to catch warnings (ie stat failures and other non-blocking errors) - archive.on('warning', function (err) { + archive.on('warning', (err) => { if (err.code !== 'ENOENT') { // throw error console.error(err) @@ -115,7 +120,7 @@ function bytesToSize (bytes) { }) // good practice to catch this error explicitly - archive.on('error', function (err) { + archive.on('error', (err) => { console.error(err) process.exit(1) }) @@ -123,7 +128,7 @@ function bytesToSize (bytes) { // pipe archive data to the file archive.pipe(output) - INCLUDE_GLOBS.forEach(glob => { + INCLUDE_GLOBS.forEach((glob) => { // append files from a glob pattern archive.glob(glob, { cwd: path.join('packages', packageDir), skip: SKIP_DIR_GLOBS }) }) diff --git a/package.json b/package.json index 3a1437c71..18cc3b6a8 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,24 @@ { "name": "vue-devtools", "version": "6.5.1", - "description": "devtools for Vue.js!", "private": true, + "description": "devtools for Vue.js!", "workspaces": [ "packages/*" ], + "author": "Evan You", + "license": "MIT", + "homepage": "https://github.com/vuejs/vue-devtools#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/vuejs/vue-devtools.git" + }, + "bugs": { + "url": "https://github.com/vuejs/vue-devtools/issues" + }, + "engines": { + "node": ">=8.10" + }, "scripts": { "dev:vue2": "concurrently \"cd packages/shell-dev-vue2 && yarn dev\" \"cd packages/shell-host && yarn dev\"", "dev:vue3": "concurrently \"cd packages/shell-dev-vue3 && yarn dev\" \"cd packages/shell-host && yarn dev\"", @@ -14,7 +27,7 @@ "dev:electron": "cd packages/shell-electron && npm run dev", "build": "lerna run build", "build:watch": "lerna run build --scope @vue-devtools/app-backend* --scope @vue-devtools/shared-* --scope @vue/devtools-api && lerna run build:watch --stream --no-sort --concurrency 99", - "lint": "eslint --ext .js,.ts,.vue .", + "lint": "eslint .", "run:firefox": "web-ext run -s packages/shell-chrome -a dist -i src", "zip": "node ./extension-zips.js", "sign:firefox": "node ./sign-firefox.js", @@ -32,36 +45,17 @@ "docs:build": "cd packages/docs && vitepress build src", "docs:serve": "cd packages/docs && vitepress serve src" }, - "repository": { - "type": "git", - "url": "git+https://github.com/vuejs/vue-devtools.git" - }, - "author": "Evan You", - "license": "MIT", - "bugs": { - "url": "https://github.com/vuejs/vue-devtools/issues" - }, - "homepage": "https://github.com/vuejs/vue-devtools#readme", "devDependencies": { + "@antfu/eslint-config": "^2.6.4", "@tailwindcss/postcss7-compat": "^2.0.4", "@types/chrome": "^0.0.139", "@types/speakingurl": "^13.0.3", - "@typescript-eslint/eslint-plugin": "^4.23.0", - "@typescript-eslint/parser": "^4.23.0", - "@vue/eslint-config-standard": "^6.0.0", - "@vue/eslint-config-typescript": "^7.0.0", "archiver": "^5.3.0", "autoprefixer": "^9.1.5", "concurrently": "^5.1.0", "cross-env": "^5.2.0", "cypress": "^3.1.0", - "eslint": "^7.26.0", - "eslint-plugin-cypress": "^2.0.1", - "eslint-plugin-import": "^2.20.2", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^5.1.0", - "eslint-plugin-standard": "^5.0.0", - "eslint-plugin-vue": "^9.17.0", + "eslint": "^8.56.0", "execa": "^4.0.3", "inquirer": "^6.2.0", "lerna": "^4.0.0", @@ -69,16 +63,13 @@ "rimraf": "^3.0.2", "semver": "^5.5.1", "start-server-and-test": "^1.7.1", + "svg-inline-loader": "^0.8.2", "tailwindcss": "npm:@tailwindcss/postcss7-compat", "vue-loader": "^17.2.2", - "webpack-dev-server": "^4.0.0-beta.0", - "svg-inline-loader": "^0.8.2" + "webpack-dev-server": "^4.15.1" }, "resolutions": { "cypress": "=3.4.1", - "webpack-dev-server": "=4.0.0-rc.0" - }, - "engines": { - "node": ">=8.10" + "webpack-dev-server": "^4.15.1" } -} \ No newline at end of file +} diff --git a/packages/api/package.json b/packages/api/package.json index 9fa5e8c75..adf01cc74 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -2,24 +2,24 @@ "name": "@vue/devtools-api", "version": "6.5.1", "description": "Interact with the Vue devtools from the page", - "main": "lib/cjs/index.js", - "browser": "lib/esm/index.js", - "module": "lib/esm/index.js", - "types": "lib/esm/index.d.ts", - "sideEffects": false, "author": { "name": "Guillaume Chau" }, - "files": [ - "lib/esm", - "lib/cjs" - ], "license": "MIT", "repository": { "url": "https://github.com/vuejs/vue-devtools.git", "type": "git", "directory": "packages/api" }, + "sideEffects": false, + "main": "lib/cjs/index.js", + "browser": "lib/esm/index.js", + "module": "lib/esm/index.js", + "types": "lib/esm/index.d.ts", + "files": [ + "lib/cjs", + "lib/esm" + ], "publishConfig": { "access": "public" }, @@ -30,8 +30,8 @@ "build:watch": "yarn tsc --module es2015 --outDir lib/esm -d -w --sourceMap" }, "devDependencies": { - "@types/node": "^13.9.1", + "@types/node": "^20.11.16", "@types/webpack-env": "^1.15.1", - "typescript": "^4.5.2" + "typescript": "^5.3.3" } -} \ No newline at end of file +} diff --git a/packages/api/src/api/api.ts b/packages/api/src/api/api.ts index 0e6f73c46..ab46a7e4d 100644 --- a/packages/api/src/api/api.ts +++ b/packages/api/src/api/api.ts @@ -6,24 +6,24 @@ import type { ID } from './util.js' export interface DevtoolsPluginApi { on: Hookable - notifyComponentUpdate (instance?: ComponentInstance): void - addTimelineLayer (options: TimelineLayerOptions): void - addTimelineEvent (options: TimelineEventOptions): void - addInspector (options: CustomInspectorOptions): void - sendInspectorTree (inspectorId: string): void - sendInspectorState (inspectorId: string): void - selectInspectorNode (inspectorId: string, nodeId: string): void - getComponentBounds (instance: ComponentInstance): Promise - getComponentName (instance: ComponentInstance): Promise - getComponentInstances (app: App): Promise - highlightElement (instance: ComponentInstance): void - unhighlightElement (): void - getSettings (pluginId?: string): TSettings - now (): number + notifyComponentUpdate: (instance?: ComponentInstance) => void + addTimelineLayer: (options: TimelineLayerOptions) => void + addTimelineEvent: (options: TimelineEventOptions) => void + addInspector: (options: CustomInspectorOptions) => void + sendInspectorTree: (inspectorId: string) => void + sendInspectorState: (inspectorId: string) => void + selectInspectorNode: (inspectorId: string, nodeId: string) => void + getComponentBounds: (instance: ComponentInstance) => Promise + getComponentName: (instance: ComponentInstance) => Promise + getComponentInstances: (app: App) => Promise + highlightElement: (instance: ComponentInstance) => void + unhighlightElement: () => void + getSettings: (pluginId?: string) => TSettings + now: () => number /** - * @private Not implemented yet + * @private */ - setSettings (values: TSettings): void + setSettings: (values: TSettings) => void } export interface AppRecord { diff --git a/packages/api/src/api/component.ts b/packages/api/src/api/component.ts index d701fd21f..9b6d93486 100644 --- a/packages/api/src/api/component.ts +++ b/packages/api/src/api/component.ts @@ -56,7 +56,7 @@ export interface ComponentCustomState extends ComponentStateBase { value: CustomState } -export type CustomState = { +export interface CustomState { _custom: { type: ComponentBuiltinCustomStateTypes | string objectType?: string diff --git a/packages/api/src/api/hooks.ts b/packages/api/src/api/hooks.ts index 98e549417..8a88a619f 100644 --- a/packages/api/src/api/hooks.ts +++ b/packages/api/src/api/hooks.ts @@ -1,7 +1,8 @@ -import type { ComponentTreeNode, InspectedComponentData, ComponentInstance, ComponentDevtoolsOptions } from './component.js' +import type { ComponentDevtoolsOptions, ComponentInstance, ComponentTreeNode, InspectedComponentData } from './component.js' import type { App } from './app.js' import type { CustomInspectorNode, CustomInspectorState, TimelineEvent } from './api.js' +// eslint-disable-next-line no-restricted-syntax export const enum Hooks { TRANSFORM_CALL = 'transformCall', GET_APP_RECORD_NAME = 'getAppRecordName', @@ -34,7 +35,7 @@ export interface ComponentBounds { height: number } -export type HookPayloads = { +export interface HookPayloads { [Hooks.TRANSFORM_CALL]: { callName: string inArgs: any[] @@ -161,26 +162,26 @@ export type EditStatePayload = { export type HookHandler = (payload: TPayload, ctx: TContext) => void | Promise export interface Hookable { - transformCall (handler: HookHandler) - getAppRecordName (handler: HookHandler) - getAppRootInstance (handler: HookHandler) - registerApplication (handler: HookHandler) - walkComponentTree (handler: HookHandler) - visitComponentTree (handler: HookHandler) - walkComponentParents (handler: HookHandler) - inspectComponent (handler: HookHandler) - getComponentBounds (handler: HookHandler) - getComponentName (handler: HookHandler) - getComponentInstances (handler: HookHandler) - getElementComponent (handler: HookHandler) - getComponentRootElements (handler: HookHandler) - editComponentState (handler: HookHandler) - getComponentDevtoolsOptions (handler: HookHandler) - getComponentRenderCode (handler: HookHandler) - inspectTimelineEvent (handler: HookHandler) - timelineCleared (handler: HookHandler) - getInspectorTree (handler: HookHandler) - getInspectorState (handler: HookHandler) - editInspectorState (handler: HookHandler) - setPluginSettings (handler: HookHandler) + transformCall: (handler: HookHandler) => any + getAppRecordName: (handler: HookHandler) => any + getAppRootInstance: (handler: HookHandler) => any + registerApplication: (handler: HookHandler) => any + walkComponentTree: (handler: HookHandler) => any + visitComponentTree: (handler: HookHandler) => any + walkComponentParents: (handler: HookHandler) => any + inspectComponent: (handler: HookHandler) => any + getComponentBounds: (handler: HookHandler) => any + getComponentName: (handler: HookHandler) => any + getComponentInstances: (handler: HookHandler) => any + getElementComponent: (handler: HookHandler) => any + getComponentRootElements: (handler: HookHandler) => any + editComponentState: (handler: HookHandler) => any + getComponentDevtoolsOptions: (handler: HookHandler) => any + getComponentRenderCode: (handler: HookHandler) => any + inspectTimelineEvent: (handler: HookHandler) => any + timelineCleared: (handler: HookHandler) => any + getInspectorTree: (handler: HookHandler) => any + getInspectorState: (handler: HookHandler) => any + editInspectorState: (handler: HookHandler) => any + setPluginSettings: (handler: HookHandler) => any } diff --git a/packages/api/src/env.ts b/packages/api/src/env.ts index 84b661dd3..f5916ed2f 100644 --- a/packages/api/src/env.ts +++ b/packages/api/src/env.ts @@ -1,5 +1,5 @@ -import type { PluginDescriptor, SetupFunction } from './index.js' import type { ApiProxy } from './proxy.js' +import type { PluginDescriptor, SetupFunction } from './index.js' export interface PluginQueueItem { pluginDescriptor: PluginDescriptor @@ -12,16 +12,16 @@ interface GlobalTarget { __VUE_DEVTOOLS_PLUGIN_API_AVAILABLE__?: boolean } -export function getDevtoolsGlobalHook (): any { +export function getDevtoolsGlobalHook(): any { return (getTarget() as any).__VUE_DEVTOOLS_GLOBAL_HOOK__ } -export function getTarget (): GlobalTarget { - // @ts-ignore +export function getTarget(): GlobalTarget { + // @ts-expect-error navigator and windows are not available in all environments return (typeof navigator !== 'undefined' && typeof window !== 'undefined') ? window - : typeof global !== 'undefined' - ? global + : typeof globalThis !== 'undefined' + ? globalThis : {} } diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index 0ef4e1cbb..c929458de 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -1,8 +1,8 @@ -import { getTarget, getDevtoolsGlobalHook, isProxyAvailable } from './env.js' +import { getDevtoolsGlobalHook, getTarget, isProxyAvailable } from './env.js' import { HOOK_SETUP } from './const.js' import type { DevtoolsPluginApi } from './api/index.js' import { ApiProxy } from './proxy.js' -import type { PluginDescriptor, ExtractSettingsTypes, PluginSettingsItem } from './plugin.js' +import type { ExtractSettingsTypes, PluginDescriptor, PluginSettingsItem } from './plugin.js' export * from './api/index.js' export * from './plugin.js' @@ -12,15 +12,13 @@ export { PluginQueueItem } from './env.js' // https://github.com/microsoft/TypeScript/issues/30680#issuecomment-752725353 type Cast = A extends B ? A : B type Narrowable = -| string -| number -| bigint -| boolean -type Narrow = Cast }) -> + | string + | number + | bigint + | boolean +type Narrow = Cast })> // Prevent properties not in PluginDescriptor // We need this because of the `extends` in the generic TDescriptor @@ -32,15 +30,16 @@ export type SetupFunction = (api: DevtoolsPluginApi) export function setupDevtoolsPlugin< TDescriptor extends Exact, - TSettings = ExtractSettingsTypes ? S : Record : Record>, -> (pluginDescriptor: Narrow, setupFn: SetupFunction) { + TSettings = ExtractSettingsTypes ? S : Record : Record>, +>(pluginDescriptor: Narrow, setupFn: SetupFunction) { const descriptor = pluginDescriptor as unknown as PluginDescriptor const target = getTarget() const hook = getDevtoolsGlobalHook() const enableProxy = isProxyAvailable && descriptor.enableEarlyProxy if (hook && (target.__VUE_DEVTOOLS_PLUGIN_API_AVAILABLE__ || !enableProxy)) { hook.emit(HOOK_SETUP, pluginDescriptor, setupFn) - } else { + } + else { const proxy = enableProxy ? new ApiProxy(descriptor, hook) : null const list = target.__VUE_DEVTOOLS_PLUGINS__ = target.__VUE_DEVTOOLS_PLUGINS__ || [] @@ -50,6 +49,8 @@ export function setupDevtoolsPlugin< proxy, }) - if (proxy) setupFn(proxy.proxiedTarget as DevtoolsPluginApi) + if (proxy) { + setupFn(proxy.proxiedTarget as DevtoolsPluginApi) + } } } diff --git a/packages/api/src/plugin.ts b/packages/api/src/plugin.ts index 328f5f4fd..f08718793 100644 --- a/packages/api/src/plugin.ts +++ b/packages/api/src/plugin.ts @@ -35,17 +35,17 @@ export type PluginSettingsItem = { }) type InferSettingsType< - T extends PluginSettingsItem + T extends PluginSettingsItem, > = [T] extends [{ type: 'boolean' }] ? boolean : [T] extends [{ type: 'choice' }] - ? T['options'][number]['value'] - : [T] extends [{ type: 'text' }] - ? string - : unknown + ? T['options'][number]['value'] + : [T] extends [{ type: 'text' }] + ? string + : unknown export type ExtractSettingsTypes< - O extends Record + O extends Record, > = { [K in keyof O]: InferSettingsType } diff --git a/packages/api/src/proxy.ts b/packages/api/src/proxy.ts index 44c7af935..172706032 100644 --- a/packages/api/src/proxy.ts +++ b/packages/api/src/proxy.ts @@ -21,7 +21,7 @@ export class ApiProxy = DevtoolsPluginApi hook: any fallbacks: Record - constructor (plugin: PluginDescriptor, hook: any) { + constructor(plugin: PluginDescriptor, hook: any) { this.target = null this.targetQueue = [] this.onQueue = [] @@ -42,23 +42,25 @@ export class ApiProxy = DevtoolsPluginApi const raw = localStorage.getItem(localSettingsSaveId) const data = JSON.parse(raw) Object.assign(currentSettings, data) - } catch (e) { + } + catch (e) { // noop } this.fallbacks = { - getSettings () { + getSettings() { return currentSettings }, - setSettings (value) { + setSettings(value) { try { localStorage.setItem(localSettingsSaveId, JSON.stringify(value)) - } catch (e) { + } + catch (e) { // noop } currentSettings = value }, - now () { + now() { return now() }, } @@ -75,7 +77,8 @@ export class ApiProxy = DevtoolsPluginApi get: (_target, prop: string) => { if (this.target) { return this.target.on[prop] - } else { + } + else { return (...args) => { this.onQueue.push({ method: prop, @@ -90,9 +93,11 @@ export class ApiProxy = DevtoolsPluginApi get: (_target, prop: string) => { if (this.target) { return this.target[prop] - } else if (prop === 'on') { + } + else if (prop === 'on') { return this.proxiedOn - } else if (Object.keys(this.fallbacks).includes(prop)) { + } + else if (Object.keys(this.fallbacks).includes(prop)) { return (...args) => { this.targetQueue.push({ method: prop, @@ -101,9 +106,10 @@ export class ApiProxy = DevtoolsPluginApi }) return this.fallbacks[prop](...args) } - } else { + } + else { return (...args) => { - return new Promise(resolve => { + return new Promise((resolve) => { this.targetQueue.push({ method: prop, args, @@ -116,7 +122,7 @@ export class ApiProxy = DevtoolsPluginApi }) } - async setRealTarget (target: TTarget) { + async setRealTarget(target: TTarget) { this.target = target for (const item of this.onQueue) { diff --git a/packages/api/src/time.ts b/packages/api/src/time.ts index 7080caf59..87f919c6b 100644 --- a/packages/api/src/time.ts +++ b/packages/api/src/time.ts @@ -1,22 +1,24 @@ let supported: boolean let perf: Performance -export function isPerformanceSupported () { +export function isPerformanceSupported() { if (supported !== undefined) { return supported } if (typeof window !== 'undefined' && window.performance) { supported = true perf = window.performance - } else if (typeof global !== 'undefined' && (global as any).perf_hooks?.performance) { + } + else if (typeof globalThis !== 'undefined' && (globalThis as any).perf_hooks?.performance) { supported = true - perf = (global as any).perf_hooks.performance - } else { + perf = (globalThis as any).perf_hooks.performance + } + else { supported = false } return supported } -export function now () { +export function now() { return isPerformanceSupported() ? perf.now() : Date.now() } diff --git a/packages/api/tsconfig.json b/packages/api/tsconfig.json index 2a5a2092d..51b266054 100644 --- a/packages/api/tsconfig.json +++ b/packages/api/tsconfig.json @@ -1,23 +1,23 @@ { "compilerOptions": { "target": "ES2017", + "lib": ["ESNext", "DOM"], "module": "ESNext", "moduleResolution": "node", - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, - "removeComments": false, "resolveJsonModule": true, - "skipLibCheck": true, "types": ["node", "webpack-env"], - "sourceMap": false, - "preserveWatchOutput": true, + "strictBindCallApply": true, + "strictFunctionTypes": true, + "alwaysStrict": true, // Strict "noImplicitAny": false, "noImplicitThis": true, - "alwaysStrict": true, - "strictBindCallApply": true, - "strictFunctionTypes": true, - "lib": ["ESNext", "DOM"] + "removeComments": false, + "sourceMap": false, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "skipLibCheck": true, + "preserveWatchOutput": true }, "include": ["src/**/*"], "exclude": ["node_modules"] diff --git a/packages/app-backend-api/package.json b/packages/app-backend-api/package.json index 64bb1f599..a271ec931 100644 --- a/packages/app-backend-api/package.json +++ b/packages/app-backend-api/package.json @@ -10,12 +10,12 @@ "ts": "tsc -d -outDir lib" }, "dependencies": { - "@vue/devtools-api": "^6.0.0-beta.1", - "@vue-devtools/shared-utils": "^0.0.0" + "@vue-devtools/shared-utils": "^0.0.0", + "@vue/devtools-api": "^6.0.0-beta.1" }, "devDependencies": { - "@types/node": "^13.9.1", + "@types/node": "^20.11.16", "@types/webpack-env": "^1.15.1", - "typescript": "^4.5.2" + "typescript": "^5.3.3" } } diff --git a/packages/app-backend-api/src/api.ts b/packages/app-backend-api/src/api.ts index 51235fbda..c08f96cd2 100644 --- a/packages/app-backend-api/src/api.ts +++ b/packages/app-backend-api/src/api.ts @@ -1,33 +1,37 @@ -import { +import type { Bridge, - hasPluginPermission, +} from '@vue-devtools/shared-utils' +import { HookEvents, PluginPermission, + StateEditor, getPluginDefaultSettings, getPluginSettings, + hasPluginPermission, setPluginSettings, - StateEditor, } from '@vue-devtools/shared-utils' -import { - Hooks, - HookPayloads, +import type { App, - DevtoolsPluginApi, + ComponentDevtoolsOptions, ComponentInstance, - TimelineLayerOptions, - TimelineEventOptions, + ComponentTreeNode, CustomInspectorOptions, + DevtoolsPluginApi, EditStatePayload, + HookPayloads, + TimelineEventOptions, + TimelineLayerOptions, WithId, - ComponentTreeNode, - ComponentDevtoolsOptions, +} from '@vue/devtools-api' +import { + Hooks, now, } from '@vue/devtools-api' import { DevtoolsHookable } from './hooks' -import { BackendContext } from './backend-context' -import { Plugin } from './plugin' -import { DevtoolsBackend } from './backend' -import { AppRecord } from './app-record' +import type { BackendContext } from './backend-context' +import type { Plugin } from './plugin' +import type { DevtoolsBackend } from './backend' +import type { AppRecord } from './app-record' const pluginOn: DevtoolsHookable[] = [] @@ -38,14 +42,14 @@ export class DevtoolsApi { on: DevtoolsHookable stateEditor: StateEditor = new StateEditor() - constructor (backend: DevtoolsBackend, ctx: BackendContext) { + constructor(backend: DevtoolsBackend, ctx: BackendContext) { this.backend = backend this.ctx = ctx this.bridge = ctx.bridge this.on = new DevtoolsHookable(ctx) } - async callHook (eventType: T, payload: HookPayloads[T], ctx: BackendContext = this.ctx) { + async callHook(eventType: T, payload: HookPayloads[T], ctx: BackendContext = this.ctx) { payload = await this.on.callHandlers(eventType, payload, ctx) for (const on of pluginOn) { payload = await on.callHandlers(eventType, payload, ctx) @@ -53,7 +57,7 @@ export class DevtoolsApi { return payload } - async transformCall (callName: string, ...args) { + async transformCall(callName: string, ...args) { const payload = await this.callHook(Hooks.TRANSFORM_CALL, { callName, inArgs: args, @@ -62,19 +66,20 @@ export class DevtoolsApi { return payload.outArgs } - async getAppRecordName (app: App, defaultName: string): Promise { + async getAppRecordName(app: App, defaultName: string): Promise { const payload = await this.callHook(Hooks.GET_APP_RECORD_NAME, { app, name: null, }) if (payload.name) { return payload.name - } else { + } + else { return `App ${defaultName}` } } - async getAppRootInstance (app: App) { + async getAppRootInstance(app: App) { const payload = await this.callHook(Hooks.GET_APP_ROOT_INSTANCE, { app, root: null, @@ -82,13 +87,13 @@ export class DevtoolsApi { return payload.root } - async registerApplication (app: App) { + async registerApplication(app: App) { await this.callHook(Hooks.REGISTER_APPLICATION, { app, }) } - async walkComponentTree (instance: ComponentInstance, maxDepth = -1, filter: string = null, recursively = false) { + async walkComponentTree(instance: ComponentInstance, maxDepth = -1, filter: string = null, recursively = false) { const payload = await this.callHook(Hooks.WALK_COMPONENT_TREE, { componentInstance: instance, componentTreeData: null, @@ -99,7 +104,7 @@ export class DevtoolsApi { return payload.componentTreeData } - async visitComponentTree (instance: ComponentInstance, treeNode: ComponentTreeNode, filter: string = null, app: App) { + async visitComponentTree(instance: ComponentInstance, treeNode: ComponentTreeNode, filter: string = null, app: App) { const payload = await this.callHook(Hooks.VISIT_COMPONENT_TREE, { app, componentInstance: instance, @@ -109,7 +114,7 @@ export class DevtoolsApi { return payload.treeNode } - async walkComponentParents (instance: ComponentInstance) { + async walkComponentParents(instance: ComponentInstance) { const payload = await this.callHook(Hooks.WALK_COMPONENT_PARENTS, { componentInstance: instance, parentInstances: [], @@ -117,7 +122,7 @@ export class DevtoolsApi { return payload.parentInstances } - async inspectComponent (instance: ComponentInstance, app: App) { + async inspectComponent(instance: ComponentInstance, app: App) { const payload = await this.callHook(Hooks.INSPECT_COMPONENT, { app, componentInstance: instance, @@ -126,7 +131,7 @@ export class DevtoolsApi { return payload.instanceData } - async getComponentBounds (instance: ComponentInstance) { + async getComponentBounds(instance: ComponentInstance) { const payload = await this.callHook(Hooks.GET_COMPONENT_BOUNDS, { componentInstance: instance, bounds: null, @@ -134,7 +139,7 @@ export class DevtoolsApi { return payload.bounds } - async getComponentName (instance: ComponentInstance) { + async getComponentName(instance: ComponentInstance) { const payload = await this.callHook(Hooks.GET_COMPONENT_NAME, { componentInstance: instance, name: null, @@ -142,7 +147,7 @@ export class DevtoolsApi { return payload.name } - async getComponentInstances (app: App) { + async getComponentInstances(app: App) { const payload = await this.callHook(Hooks.GET_COMPONENT_INSTANCES, { app, componentInstances: [], @@ -150,7 +155,7 @@ export class DevtoolsApi { return payload.componentInstances } - async getElementComponent (element: HTMLElement | any) { + async getElementComponent(element: HTMLElement | any) { const payload = await this.callHook(Hooks.GET_ELEMENT_COMPONENT, { element, componentInstance: null, @@ -158,7 +163,7 @@ export class DevtoolsApi { return payload.componentInstance } - async getComponentRootElements (instance: ComponentInstance) { + async getComponentRootElements(instance: ComponentInstance) { const payload = await this.callHook(Hooks.GET_COMPONENT_ROOT_ELEMENTS, { componentInstance: instance, rootElements: [], @@ -166,7 +171,7 @@ export class DevtoolsApi { return payload.rootElements } - async editComponentState (instance: ComponentInstance, dotPath: string, type: string, state: EditStatePayload, app: App) { + async editComponentState(instance: ComponentInstance, dotPath: string, type: string, state: EditStatePayload, app: App) { const arrayPath = dotPath.split('.') const payload = await this.callHook(Hooks.EDIT_COMPONENT_STATE, { app, @@ -179,7 +184,7 @@ export class DevtoolsApi { return payload.componentInstance } - async getComponentDevtoolsOptions (instance: ComponentInstance): Promise { + async getComponentDevtoolsOptions(instance: ComponentInstance): Promise { const payload = await this.callHook(Hooks.GET_COMPONENT_DEVTOOLS_OPTIONS, { componentInstance: instance, options: null, @@ -187,7 +192,7 @@ export class DevtoolsApi { return payload.options || {} } - async getComponentRenderCode (instance: ComponentInstance): Promise<{ + async getComponentRenderCode(instance: ComponentInstance): Promise<{ code: string }> { const payload = await this.callHook(Hooks.GET_COMPONENT_RENDER_CODE, { @@ -199,7 +204,7 @@ export class DevtoolsApi { } } - async inspectTimelineEvent (eventData: TimelineEventOptions & WithId, app: App) { + async inspectTimelineEvent(eventData: TimelineEventOptions & WithId, app: App) { const payload = await this.callHook(Hooks.INSPECT_TIMELINE_EVENT, { event: eventData.event, layerId: eventData.layerId, @@ -210,11 +215,11 @@ export class DevtoolsApi { return payload.data } - async clearTimeline () { + async clearTimeline() { await this.callHook(Hooks.TIMELINE_CLEARED, {}) } - async getInspectorTree (inspectorId: string, app: App, filter: string) { + async getInspectorTree(inspectorId: string, app: App, filter: string) { const payload = await this.callHook(Hooks.GET_INSPECTOR_TREE, { inspectorId, app, @@ -224,7 +229,7 @@ export class DevtoolsApi { return payload.rootNodes } - async getInspectorState (inspectorId: string, app: App, nodeId: string) { + async getInspectorState(inspectorId: string, app: App, nodeId: string) { const payload = await this.callHook(Hooks.GET_INSPECTOR_STATE, { inspectorId, app, @@ -234,7 +239,7 @@ export class DevtoolsApi { return payload.state } - async editInspectorState (inspectorId: string, app: App, nodeId: string, dotPath: string, type: string, state: EditStatePayload) { + async editInspectorState(inspectorId: string, app: App, nodeId: string, dotPath: string, type: string, state: EditStatePayload) { const arrayPath = dotPath.split('.') await this.callHook(Hooks.EDIT_INSPECTOR_STATE, { inspectorId, @@ -247,7 +252,7 @@ export class DevtoolsApi { }) } - now () { + now() { return now() } } @@ -261,7 +266,7 @@ export class DevtoolsPluginApiInstance implements DevtoolsPlugi on: DevtoolsHookable private defaultSettings: TSettings - constructor (plugin: Plugin, appRecord: AppRecord, ctx: BackendContext) { + constructor(plugin: Plugin, appRecord: AppRecord, ctx: BackendContext) { this.bridge = ctx.bridge this.ctx = ctx this.plugin = plugin @@ -274,101 +279,120 @@ export class DevtoolsPluginApiInstance implements DevtoolsPlugi // Plugin API - async notifyComponentUpdate (instance: ComponentInstance = null) { - if (!this.enabled || !this.hasPermission(PluginPermission.COMPONENTS)) return + async notifyComponentUpdate(instance: ComponentInstance = null) { + if (!this.enabled || !this.hasPermission(PluginPermission.COMPONENTS)) { + return + } if (instance) { this.ctx.hook.emit(HookEvents.COMPONENT_UPDATED, ...await this.backendApi.transformCall(HookEvents.COMPONENT_UPDATED, instance)) - } else { + } + else { this.ctx.hook.emit(HookEvents.COMPONENT_UPDATED) } } - addTimelineLayer (options: TimelineLayerOptions) { - if (!this.enabled || !this.hasPermission(PluginPermission.TIMELINE)) return false + addTimelineLayer(options: TimelineLayerOptions) { + if (!this.enabled || !this.hasPermission(PluginPermission.TIMELINE)) { + return false + } this.ctx.hook.emit(HookEvents.TIMELINE_LAYER_ADDED, options, this.plugin) return true } - addTimelineEvent (options: TimelineEventOptions) { - if (!this.enabled || !this.hasPermission(PluginPermission.TIMELINE)) return false + addTimelineEvent(options: TimelineEventOptions) { + if (!this.enabled || !this.hasPermission(PluginPermission.TIMELINE)) { + return false + } this.ctx.hook.emit(HookEvents.TIMELINE_EVENT_ADDED, options, this.plugin) return true } - addInspector (options: CustomInspectorOptions) { - if (!this.enabled || !this.hasPermission(PluginPermission.CUSTOM_INSPECTOR)) return false + addInspector(options: CustomInspectorOptions) { + if (!this.enabled || !this.hasPermission(PluginPermission.CUSTOM_INSPECTOR)) { + return false + } this.ctx.hook.emit(HookEvents.CUSTOM_INSPECTOR_ADD, options, this.plugin) return true } - sendInspectorTree (inspectorId: string) { - if (!this.enabled || !this.hasPermission(PluginPermission.CUSTOM_INSPECTOR)) return false + sendInspectorTree(inspectorId: string) { + if (!this.enabled || !this.hasPermission(PluginPermission.CUSTOM_INSPECTOR)) { + return false + } this.ctx.hook.emit(HookEvents.CUSTOM_INSPECTOR_SEND_TREE, inspectorId, this.plugin) return true } - sendInspectorState (inspectorId: string) { - if (!this.enabled || !this.hasPermission(PluginPermission.CUSTOM_INSPECTOR)) return false + sendInspectorState(inspectorId: string) { + if (!this.enabled || !this.hasPermission(PluginPermission.CUSTOM_INSPECTOR)) { + return false + } this.ctx.hook.emit(HookEvents.CUSTOM_INSPECTOR_SEND_STATE, inspectorId, this.plugin) return true } - selectInspectorNode (inspectorId: string, nodeId: string) { - if (!this.enabled || !this.hasPermission(PluginPermission.CUSTOM_INSPECTOR)) return false + selectInspectorNode(inspectorId: string, nodeId: string) { + if (!this.enabled || !this.hasPermission(PluginPermission.CUSTOM_INSPECTOR)) { + return false + } this.ctx.hook.emit(HookEvents.CUSTOM_INSPECTOR_SELECT_NODE, inspectorId, nodeId, this.plugin) return true } - getComponentBounds (instance: ComponentInstance) { + getComponentBounds(instance: ComponentInstance) { return this.backendApi.getComponentBounds(instance) } - getComponentName (instance: ComponentInstance) { + getComponentName(instance: ComponentInstance) { return this.backendApi.getComponentName(instance) } - getComponentInstances (app: App) { + getComponentInstances(app: App) { return this.backendApi.getComponentInstances(app) } - highlightElement (instance: ComponentInstance) { - if (!this.enabled || !this.hasPermission(PluginPermission.COMPONENTS)) return false + highlightElement(instance: ComponentInstance) { + if (!this.enabled || !this.hasPermission(PluginPermission.COMPONENTS)) { + return false + } this.ctx.hook.emit(HookEvents.COMPONENT_HIGHLIGHT, instance.__VUE_DEVTOOLS_UID__, this.plugin) return true } - unhighlightElement () { - if (!this.enabled || !this.hasPermission(PluginPermission.COMPONENTS)) return false + unhighlightElement() { + if (!this.enabled || !this.hasPermission(PluginPermission.COMPONENTS)) { + return false + } this.ctx.hook.emit(HookEvents.COMPONENT_UNHIGHLIGHT, this.plugin) return true } - getSettings (pluginId?: string) { + getSettings(pluginId?: string) { return getPluginSettings(pluginId ?? this.plugin.descriptor.id, this.defaultSettings) } - setSettings (value: TSettings, pluginId?: string) { + setSettings(value: TSettings, pluginId?: string) { setPluginSettings(pluginId ?? this.plugin.descriptor.id, value) } - now () { + now() { return now() } - private get enabled () { + private get enabled() { return hasPluginPermission(this.plugin.descriptor.id, PluginPermission.ENABLED) } - private hasPermission (permission: PluginPermission) { + private hasPermission(permission: PluginPermission) { return hasPluginPermission(this.plugin.descriptor.id, permission) } } diff --git a/packages/app-backend-api/src/app-record.ts b/packages/app-backend-api/src/app-record.ts index 9d7e9f975..d61f80b6e 100644 --- a/packages/app-backend-api/src/app-record.ts +++ b/packages/app-backend-api/src/app-record.ts @@ -1,5 +1,5 @@ -import { DevtoolsBackend } from './backend' -import { App, ComponentInstance, TimelineEventOptions, ID, WithId } from '@vue/devtools-api' +import type { App, ComponentInstance } from '@vue/devtools-api' +import type { DevtoolsBackend } from './backend' export interface AppRecordOptions { app: App diff --git a/packages/app-backend-api/src/backend-context.ts b/packages/app-backend-api/src/backend-context.ts index db9753fd4..d878ec063 100644 --- a/packages/app-backend-api/src/backend-context.ts +++ b/packages/app-backend-api/src/backend-context.ts @@ -1,16 +1,16 @@ -import { Bridge } from '@vue-devtools/shared-utils' -import { - TimelineLayerOptions, +import type { Bridge } from '@vue-devtools/shared-utils' +import type { CustomInspectorOptions, - TimelineEventOptions, - WithId, ID, + TimelineEventOptions, + TimelineLayerOptions, TimelineMarkerOptions, + WithId, } from '@vue/devtools-api' -import { AppRecord } from './app-record' -import { Plugin } from './plugin' -import { DevtoolsHook } from './global-hook' -import { DevtoolsBackend } from './backend' +import type { AppRecord } from './app-record' +import type { Plugin } from './plugin' +import type { DevtoolsHook } from './global-hook' +import type { DevtoolsBackend } from './backend' export interface BackendContext { bridge: Bridge @@ -52,7 +52,7 @@ export interface CreateBackendContextOptions { hook: DevtoolsHook } -export function createBackendContext (options: CreateBackendContextOptions): BackendContext { +export function createBackendContext(options: CreateBackendContextOptions): BackendContext { return { bridge: options.bridge, hook: options.hook, diff --git a/packages/app-backend-api/src/backend.ts b/packages/app-backend-api/src/backend.ts index 97210a789..67e4975af 100644 --- a/packages/app-backend-api/src/backend.ts +++ b/packages/app-backend-api/src/backend.ts @@ -1,12 +1,12 @@ -import { AppRecord } from './app-record' +import type { AppRecord } from './app-record' import { DevtoolsApi } from './api' -import { BackendContext } from './backend-context' +import type { BackendContext } from './backend-context' export enum BuiltinBackendFeature { /** * @deprecated */ - FLUSH = 'flush' + FLUSH = 'flush', } export interface DevtoolsBackendOptions { @@ -16,7 +16,7 @@ export interface DevtoolsBackendOptions { setupApp?: (api: DevtoolsApi, app: AppRecord) => void } -export function defineBackend (options: DevtoolsBackendOptions) { +export function defineBackend(options: DevtoolsBackendOptions) { return options } @@ -25,7 +25,7 @@ export interface DevtoolsBackend { api: DevtoolsApi } -export function createBackend (options: DevtoolsBackendOptions, ctx: BackendContext): DevtoolsBackend { +export function createBackend(options: DevtoolsBackendOptions, ctx: BackendContext): DevtoolsBackend { const backend: DevtoolsBackend = { options, api: null, diff --git a/packages/app-backend-api/src/global-hook.ts b/packages/app-backend-api/src/global-hook.ts index 3596d17df..5c07e85c5 100644 --- a/packages/app-backend-api/src/global-hook.ts +++ b/packages/app-backend-api/src/global-hook.ts @@ -1,6 +1,4 @@ -/* eslint-disable @typescript-eslint/ban-types */ - -import { AppRecordOptions } from './app-record' +import type { AppRecordOptions } from './app-record' export interface DevtoolsHook { emit: (event: string, ...payload: any[]) => void diff --git a/packages/app-backend-api/src/hooks.ts b/packages/app-backend-api/src/hooks.ts index 41b83afc6..3cbf86cbb 100644 --- a/packages/app-backend-api/src/hooks.ts +++ b/packages/app-backend-api/src/hooks.ts @@ -1,7 +1,8 @@ -import { hasPluginPermission, PluginPermission } from '@vue-devtools/shared-utils' -import { Hooks, HookPayloads, Hookable, HookHandler } from '@vue/devtools-api' -import { BackendContext } from './backend-context' -import { Plugin } from './plugin' +import { PluginPermission, hasPluginPermission } from '@vue-devtools/shared-utils' +import type { HookHandler, HookPayloads, Hookable } from '@vue/devtools-api' +import { Hooks } from '@vue/devtools-api' +import type { BackendContext } from './backend-context' +import type { Plugin } from './plugin' type Handler = HookHandler @@ -15,29 +16,29 @@ export class DevtoolsHookable implements Hookable { private ctx: BackendContext private plugin: Plugin - constructor (ctx: BackendContext, plugin: Plugin = null) { + constructor(ctx: BackendContext, plugin: Plugin = null) { this.ctx = ctx this.plugin = plugin } - private hook (eventType: T, handler: Handler, pluginPermision: PluginPermission = null) { + private hook(eventType: T, handler: Handler, pluginPermision: PluginPermission = null) { const handlers = (this.handlers[eventType] = this.handlers[eventType] || []) as HookHandlerData[] if (this.plugin) { const originalHandler = handler handler = (...args) => { // Plugin permission - if (!hasPluginPermission(this.plugin.descriptor.id, PluginPermission.ENABLED) || - (pluginPermision && !hasPluginPermission(this.plugin.descriptor.id, pluginPermision)) - ) return + if (!hasPluginPermission(this.plugin.descriptor.id, PluginPermission.ENABLED) + || (pluginPermision && !hasPluginPermission(this.plugin.descriptor.id, pluginPermision)) + ) { return } // App scope - if (!this.plugin.descriptor.disableAppScope && - this.ctx.currentAppRecord?.options.app !== this.plugin.descriptor.app) return + if (!this.plugin.descriptor.disableAppScope + && this.ctx.currentAppRecord?.options.app !== this.plugin.descriptor.app) { return } // Plugin scope - if (!this.plugin.descriptor.disablePluginScope && - (args[0] as any).pluginId != null && (args[0] as any).pluginId !== this.plugin.descriptor.id) return + if (!this.plugin.descriptor.disablePluginScope + && (args[0] as any).pluginId != null && (args[0] as any).pluginId !== this.plugin.descriptor.id) { return } return originalHandler(...args) } @@ -49,14 +50,15 @@ export class DevtoolsHookable implements Hookable { }) } - async callHandlers (eventType: T, payload: HookPayloads[T], ctx: BackendContext) { + async callHandlers(eventType: T, payload: HookPayloads[T], ctx: BackendContext) { if (this.handlers[eventType]) { const handlers = this.handlers[eventType] as HookHandlerData[] for (let i = 0; i < handlers.length; i++) { const { handler, plugin } = handlers[i] try { await handler(payload, ctx) - } catch (e) { + } + catch (e) { console.error(`An error occurred in hook '${eventType}'${plugin ? ` registered by plugin '${plugin.descriptor.id}'` : ''} with payload:`, payload) console.error(e) } @@ -65,91 +67,91 @@ export class DevtoolsHookable implements Hookable { return payload } - transformCall (handler: Handler) { + transformCall(handler: Handler) { this.hook(Hooks.TRANSFORM_CALL, handler) } - getAppRecordName (handler: Handler) { + getAppRecordName(handler: Handler) { this.hook(Hooks.GET_APP_RECORD_NAME, handler) } - getAppRootInstance (handler: Handler) { + getAppRootInstance(handler: Handler) { this.hook(Hooks.GET_APP_ROOT_INSTANCE, handler) } - registerApplication (handler: Handler) { + registerApplication(handler: Handler) { this.hook(Hooks.REGISTER_APPLICATION, handler) } - walkComponentTree (handler: Handler) { + walkComponentTree(handler: Handler) { this.hook(Hooks.WALK_COMPONENT_TREE, handler, PluginPermission.COMPONENTS) } - visitComponentTree (handler: Handler) { + visitComponentTree(handler: Handler) { this.hook(Hooks.VISIT_COMPONENT_TREE, handler, PluginPermission.COMPONENTS) } - walkComponentParents (handler: Handler) { + walkComponentParents(handler: Handler) { this.hook(Hooks.WALK_COMPONENT_PARENTS, handler, PluginPermission.COMPONENTS) } - inspectComponent (handler: Handler) { + inspectComponent(handler: Handler) { this.hook(Hooks.INSPECT_COMPONENT, handler, PluginPermission.COMPONENTS) } - getComponentBounds (handler: Handler) { + getComponentBounds(handler: Handler) { this.hook(Hooks.GET_COMPONENT_BOUNDS, handler, PluginPermission.COMPONENTS) } - getComponentName (handler: Handler) { + getComponentName(handler: Handler) { this.hook(Hooks.GET_COMPONENT_NAME, handler, PluginPermission.COMPONENTS) } - getComponentInstances (handler: Handler) { + getComponentInstances(handler: Handler) { this.hook(Hooks.GET_COMPONENT_INSTANCES, handler, PluginPermission.COMPONENTS) } - getElementComponent (handler: Handler) { + getElementComponent(handler: Handler) { this.hook(Hooks.GET_ELEMENT_COMPONENT, handler, PluginPermission.COMPONENTS) } - getComponentRootElements (handler: Handler) { + getComponentRootElements(handler: Handler) { this.hook(Hooks.GET_COMPONENT_ROOT_ELEMENTS, handler, PluginPermission.COMPONENTS) } - editComponentState (handler: Handler) { + editComponentState(handler: Handler) { this.hook(Hooks.EDIT_COMPONENT_STATE, handler, PluginPermission.COMPONENTS) } - getComponentDevtoolsOptions (handler: Handler) { + getComponentDevtoolsOptions(handler: Handler) { this.hook(Hooks.GET_COMPONENT_DEVTOOLS_OPTIONS, handler, PluginPermission.COMPONENTS) } - getComponentRenderCode (handler: Handler) { + getComponentRenderCode(handler: Handler) { this.hook(Hooks.GET_COMPONENT_RENDER_CODE, handler, PluginPermission.COMPONENTS) } - inspectTimelineEvent (handler: Handler) { + inspectTimelineEvent(handler: Handler) { this.hook(Hooks.INSPECT_TIMELINE_EVENT, handler, PluginPermission.TIMELINE) } - timelineCleared (handler: Handler) { + timelineCleared(handler: Handler) { this.hook(Hooks.TIMELINE_CLEARED, handler, PluginPermission.TIMELINE) } - getInspectorTree (handler: Handler) { + getInspectorTree(handler: Handler) { this.hook(Hooks.GET_INSPECTOR_TREE, handler, PluginPermission.CUSTOM_INSPECTOR) } - getInspectorState (handler: Handler) { + getInspectorState(handler: Handler) { this.hook(Hooks.GET_INSPECTOR_STATE, handler, PluginPermission.CUSTOM_INSPECTOR) } - editInspectorState (handler: Handler) { + editInspectorState(handler: Handler) { this.hook(Hooks.EDIT_INSPECTOR_STATE, handler, PluginPermission.CUSTOM_INSPECTOR) } - setPluginSettings (handler: Handler) { + setPluginSettings(handler: Handler) { this.hook(Hooks.SET_PLUGIN_SETTINGS, handler) } } diff --git a/packages/app-backend-api/src/plugin.ts b/packages/app-backend-api/src/plugin.ts index 4fde11981..f9df976c1 100644 --- a/packages/app-backend-api/src/plugin.ts +++ b/packages/app-backend-api/src/plugin.ts @@ -1,4 +1,4 @@ -import { PluginDescriptor, SetupFunction } from '@vue/devtools-api' +import type { PluginDescriptor, SetupFunction } from '@vue/devtools-api' export interface Plugin { descriptor: PluginDescriptor diff --git a/packages/app-backend-api/tsconfig.json b/packages/app-backend-api/tsconfig.json index f565bc660..becb763b2 100644 --- a/packages/app-backend-api/tsconfig.json +++ b/packages/app-backend-api/tsconfig.json @@ -3,25 +3,25 @@ "target": "ES2019", "module": "commonjs", "moduleResolution": "node", - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, "resolveJsonModule": true, - "skipLibCheck": true, "types": [ "node", "webpack-env" ], - "sourceMap": true, - "preserveWatchOutput": true, + "strictBindCallApply": true, + "strictFunctionTypes": true, + "alwaysStrict": true, // Strict "noImplicitAny": false, "noImplicitThis": true, - "alwaysStrict": true, - "strictBindCallApply": true, - "strictFunctionTypes": true, + "sourceMap": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "skipLibCheck": true, + "preserveWatchOutput": true }, "include": [ - "src/**/*", + "src/**/*" ], "exclude": [ "node_modules" diff --git a/packages/app-backend-core/package.json b/packages/app-backend-core/package.json index ce1e5093a..4249524c3 100644 --- a/packages/app-backend-core/package.json +++ b/packages/app-backend-core/package.json @@ -10,18 +10,18 @@ "ts": "tsc -d -outDir lib" }, "dependencies": { - "@vue/devtools-api": "^6.0.0-beta.1", "@vue-devtools/app-backend-api": "^0.0.0", "@vue-devtools/app-backend-vue1": "^0.0.0", "@vue-devtools/app-backend-vue2": "^0.0.0", "@vue-devtools/app-backend-vue3": "^0.0.0", "@vue-devtools/shared-utils": "^0.0.0", + "@vue/devtools-api": "^6.0.0-beta.1", "lodash": "^4.17.21", "speakingurl": "^14.0.1" }, "devDependencies": { - "@types/node": "^13.9.1", + "@types/node": "^20.11.16", "@types/webpack-env": "^1.15.1", - "typescript": "^4.5.2" + "typescript": "^5.3.3" } } diff --git a/packages/app-backend-core/src/app.ts b/packages/app-backend-core/src/app.ts index 3546cdae5..fc1fbb7e8 100644 --- a/packages/app-backend-core/src/app.ts +++ b/packages/app-backend-core/src/app.ts @@ -1,17 +1,17 @@ -import { +import type { AppRecord, - SimpleAppRecord, AppRecordOptions, BackendContext, DevtoolsBackend, + SimpleAppRecord, } from '@vue-devtools/app-backend-api' -import { BridgeEvents, isBrowser, SharedData } from '@vue-devtools/shared-utils' -import { App } from '@vue/devtools-api' +import { BridgeEvents, SharedData, isBrowser } from '@vue-devtools/shared-utils' +import type { App } from '@vue/devtools-api' import slug from 'speakingurl' import { JobQueue } from './util/queue' import { scan } from './legacy/scan' import { addBuiltinLayers, removeLayersForApp } from './timeline' -import { getBackend, availableBackends } from './backend' +import { availableBackends, getBackend } from './backend' import { hook } from './global-hook.js' const jobs = new JobQueue() @@ -21,11 +21,11 @@ let recordId = 0 type AppRecordResolver = (record: AppRecord) => void | Promise const appRecordPromises = new Map() -export async function registerApp (options: AppRecordOptions, ctx: BackendContext) { +export async function registerApp(options: AppRecordOptions, ctx: BackendContext) { return jobs.queue('regiserApp', () => registerAppJob(options, ctx)) } -async function registerAppJob (options: AppRecordOptions, ctx: BackendContext) { +async function registerAppJob(options: AppRecordOptions, ctx: BackendContext) { // Dedupe if (ctx.appRecords.find(a => a.options.app === options.app)) { return @@ -36,7 +36,7 @@ async function registerAppJob (options: AppRecordOptions, ctx: BackendContext) { } // Find correct backend - const baseFrameworkVersion = parseInt(options.version.substring(0, options.version.indexOf('.'))) + const baseFrameworkVersion = Number.parseInt(options.version.substring(0, options.version.indexOf('.'))) for (let i = 0; i < availableBackends.length; i++) { const backendOptions = availableBackends[i] if (backendOptions.frameworkVersion === baseFrameworkVersion) { @@ -50,7 +50,7 @@ async function registerAppJob (options: AppRecordOptions, ctx: BackendContext) { } } -async function createAppRecord (options: AppRecordOptions, backend: DevtoolsBackend, ctx: BackendContext) { +async function createAppRecord(options: AppRecordOptions, backend: DevtoolsBackend, ctx: BackendContext) { const rootInstance = await backend.api.getAppRootInstance(options.app) if (rootInstance) { if ((await backend.api.getComponentDevtoolsOptions(rootInstance)).hide) { @@ -107,12 +107,13 @@ async function createAppRecord (options: AppRecordOptions, backend: DevtoolsBack await r(record) } } - } else if (SharedData.debugInfo) { + } + else if (SharedData.debugInfo) { console.warn('[Vue devtools] No root instance found for app, it might have been unmounted', options.app) } } -export async function selectApp (record: AppRecord, ctx: BackendContext) { +export async function selectApp(record: AppRecord, ctx: BackendContext) { ctx.currentAppRecord = record ctx.currentInspectedComponentId = record.lastInspectedComponentId ctx.bridge.send(BridgeEvents.TO_FRONT_APP_SELECTED, { @@ -121,7 +122,7 @@ export async function selectApp (record: AppRecord, ctx: BackendContext) { }) } -export function mapAppRecord (record: AppRecord): SimpleAppRecord { +export function mapAppRecord(record: AppRecord): SimpleAppRecord { return { id: record.id, name: record.name, @@ -132,7 +133,7 @@ export function mapAppRecord (record: AppRecord): SimpleAppRecord { const appIds = new Set() -export function getAppRecordId (app, defaultId?: string): string { +export function getAppRecordId(app, defaultId?: string): string { if (app.__VUE_DEVTOOLS_APP_RECORD_ID__ != null) { return app.__VUE_DEVTOOLS_APP_RECORD_ID__ } @@ -152,12 +153,14 @@ export function getAppRecordId (app, defaultId?: string): string { return id } -export async function getAppRecord (app: any, ctx: BackendContext): Promise { +export async function getAppRecord(app: any, ctx: BackendContext): Promise { const record = app.__VUE_DEVTOOLS_APP_RECORD__ ?? ctx.appRecords.find(ar => ar.options.app === app) if (record) { return record } - if (app._vueDevtools_hidden_) return null + if (app._vueDevtools_hidden_) { + return null + } return new Promise((resolve, reject) => { let resolvers = appRecordPromises.get(app) let timedOut = false @@ -165,6 +168,7 @@ export async function getAppRecord (app: any, ctx: BackendContext): Promise { if (!timedOut) { clearTimeout(timer) @@ -172,10 +176,12 @@ export async function getAppRecord (app: any, ctx: BackendContext): Promise { + timer = setTimeout(() => { timedOut = true const index = resolvers.indexOf(fn) - if (index !== -1) resolvers.splice(index, 1) + if (index !== -1) { + resolvers.splice(index, 1) + } if (SharedData.debugInfo) { // eslint-disable-next-line no-console console.log('Timed out waiting for app record', app) @@ -185,11 +191,11 @@ export async function getAppRecord (app: any, ctx: BackendContext): Promise { /* NOOP */ }) } -export async function sendApps (ctx: BackendContext) { +export async function sendApps(ctx: BackendContext) { const appRecords = [] for (const appRecord of ctx.appRecords) { @@ -201,27 +207,31 @@ export async function sendApps (ctx: BackendContext) { }) } -function removeAppRecord (appRecord: AppRecord, ctx: BackendContext) { +function removeAppRecord(appRecord: AppRecord, ctx: BackendContext) { try { appIds.delete(appRecord.id) const index = ctx.appRecords.indexOf(appRecord) - if (index !== -1) ctx.appRecords.splice(index, 1) + if (index !== -1) { + ctx.appRecords.splice(index, 1) + } removeLayersForApp(appRecord.options.app, ctx) ctx.bridge.send(BridgeEvents.TO_FRONT_APP_REMOVE, { id: appRecord.id }) - } catch (e) { + } + catch (e) { if (SharedData.debugInfo) { console.error(e) } } } -export async function removeApp (app: App, ctx: BackendContext) { +export async function removeApp(app: App, ctx: BackendContext) { try { const appRecord = await getAppRecord(app, ctx) if (appRecord) { removeAppRecord(appRecord, ctx) } - } catch (e) { + } + catch (e) { if (SharedData.debugInfo) { console.error(e) } @@ -230,13 +240,12 @@ export async function removeApp (app: App, ctx: BackendContext) { let scanTimeout: any -// eslint-disable-next-line camelcase -export function _legacy_getAndRegisterApps (ctx: BackendContext, clear = false) { +export function _legacy_getAndRegisterApps(ctx: BackendContext, clear = false) { setTimeout(() => { try { if (clear) { // Remove apps that are legacy - ctx.appRecords.forEach(appRecord => { + ctx.appRecords.forEach((appRecord) => { if (appRecord.meta.Vue) { removeAppRecord(appRecord, ctx) } @@ -250,7 +259,7 @@ export function _legacy_getAndRegisterApps (ctx: BackendContext, clear = false) scanTimeout = setTimeout(() => _legacy_getAndRegisterApps(ctx), 1000) } - apps.forEach(app => { + apps.forEach((app) => { const Vue = hook.Vue registerApp({ app, @@ -261,7 +270,8 @@ export function _legacy_getAndRegisterApps (ctx: BackendContext, clear = false) }, }, ctx) }) - } catch (e) { + } + catch (e) { console.error(`Error scanning for legacy apps:`) console.error(e) } diff --git a/packages/app-backend-core/src/backend.ts b/packages/app-backend-core/src/backend.ts index 3fd43362f..af07e8244 100644 --- a/packages/app-backend-core/src/backend.ts +++ b/packages/app-backend-core/src/backend.ts @@ -1,5 +1,5 @@ - -import { DevtoolsBackendOptions, DevtoolsBackend, createBackend, BackendContext } from '@vue-devtools/app-backend-api' +import type { BackendContext, DevtoolsBackend, DevtoolsBackendOptions } from '@vue-devtools/app-backend-api' +import { createBackend } from '@vue-devtools/app-backend-api' import { backend as backendVue1 } from '@vue-devtools/app-backend-vue1' import { backend as backendVue2 } from '@vue-devtools/app-backend-vue2' @@ -15,7 +15,7 @@ export const availableBackends = [ const enabledBackends: Map = new Map() -export function getBackend (backendOptions: DevtoolsBackendOptions, ctx: BackendContext) { +export function getBackend(backendOptions: DevtoolsBackendOptions, ctx: BackendContext) { let backend: DevtoolsBackend if (!enabledBackends.has(backendOptions)) { // Create backend @@ -23,7 +23,8 @@ export function getBackend (backendOptions: DevtoolsBackendOptions, ctx: Backend handleAddPerformanceTag(backend, ctx) enabledBackends.set(backendOptions, backend) ctx.backends.push(backend) - } else { + } + else { backend = enabledBackends.get(backendOptions) } return backend diff --git a/packages/app-backend-core/src/component-pick.ts b/packages/app-backend-core/src/component-pick.ts index 24233d79c..ca23a94fa 100644 --- a/packages/app-backend-core/src/component-pick.ts +++ b/packages/app-backend-core/src/component-pick.ts @@ -1,14 +1,14 @@ -import { isBrowser, BridgeEvents } from '@vue-devtools/shared-utils' -import { BackendContext, DevtoolsBackend } from '@vue-devtools/app-backend-api' +import { BridgeEvents, isBrowser } from '@vue-devtools/shared-utils' +import type { BackendContext, DevtoolsBackend } from '@vue-devtools/app-backend-api' +import type { ComponentInstance } from '@vue/devtools-api' import { highlight, unHighlight } from './highlighter' -import { ComponentInstance } from '@vue/devtools-api' export default class ComponentPicker { ctx: BackendContext selectedInstance: ComponentInstance selectedBackend: DevtoolsBackend - constructor (ctx: BackendContext) { + constructor(ctx: BackendContext) { this.ctx = ctx this.bindMethods() } @@ -16,8 +16,10 @@ export default class ComponentPicker { /** * Adds event listeners for mouseover and mouseup */ - startSelecting () { - if (!isBrowser) return + startSelecting() { + if (!isBrowser) { + return + } window.addEventListener('mouseover', this.elementMouseOver, true) window.addEventListener('click', this.elementClicked, true) window.addEventListener('mouseout', this.cancelEvent, true) @@ -30,8 +32,10 @@ export default class ComponentPicker { /** * Removes event listeners */ - stopSelecting () { - if (!isBrowser) return + stopSelecting() { + if (!isBrowser) { + return + } window.removeEventListener('mouseover', this.elementMouseOver, true) window.removeEventListener('click', this.elementClicked, true) window.removeEventListener('mouseout', this.cancelEvent, true) @@ -46,7 +50,7 @@ export default class ComponentPicker { /** * Highlights a component on element mouse over */ - async elementMouseOver (e: MouseEvent) { + async elementMouseOver(e: MouseEvent) { this.cancelEvent(e) const el = e.target @@ -60,7 +64,7 @@ export default class ComponentPicker { } } - async selectElementComponent (el) { + async selectElementComponent(el) { for (const backend of this.ctx.backends) { const instance = await backend.api.getElementComponent(el) if (instance) { @@ -76,13 +80,14 @@ export default class ComponentPicker { /** * Selects an instance in the component view */ - async elementClicked (e: MouseEvent) { + async elementClicked(e: MouseEvent) { this.cancelEvent(e) if (this.selectedInstance && this.selectedBackend) { const parentInstances = await this.selectedBackend.api.walkComponentParents(this.selectedInstance) this.ctx.bridge.send(BridgeEvents.TO_FRONT_COMPONENT_PICK, { id: this.selectedInstance.__VUE_DEVTOOLS_UID__, parentIds: parentInstances.map(i => i.__VUE_DEVTOOLS_UID__) }) - } else { + } + else { this.ctx.bridge.send(BridgeEvents.TO_FRONT_COMPONENT_PICK_CANCELED, null) } @@ -92,7 +97,7 @@ export default class ComponentPicker { /** * Cancel a mouse event */ - cancelEvent (e: MouseEvent) { + cancelEvent(e: MouseEvent) { e.stopImmediatePropagation() e.preventDefault() } @@ -100,7 +105,7 @@ export default class ComponentPicker { /** * Bind class methods to the class scope to avoid rebind for event listeners */ - bindMethods () { + bindMethods() { this.startSelecting = this.startSelecting.bind(this) this.stopSelecting = this.stopSelecting.bind(this) this.elementMouseOver = this.elementMouseOver.bind(this) diff --git a/packages/app-backend-core/src/component.ts b/packages/app-backend-core/src/component.ts index 2559250ee..b6e47c579 100644 --- a/packages/app-backend-core/src/component.ts +++ b/packages/app-backend-core/src/component.ts @@ -1,19 +1,22 @@ -import { stringify, BridgeEvents, parse, SharedData, createThrottleQueue } from '@vue-devtools/shared-utils' -import { AppRecord, BackendContext, BuiltinBackendFeature } from '@vue-devtools/app-backend-api' +import { BridgeEvents, SharedData, createThrottleQueue, parse, stringify } from '@vue-devtools/shared-utils' +import type { AppRecord, BackendContext } from '@vue-devtools/app-backend-api' +import { BuiltinBackendFeature } from '@vue-devtools/app-backend-api' +import type { App, ComponentInstance, EditStatePayload } from '@vue/devtools-api' import { getAppRecord } from './app' -import { App, ComponentInstance, EditStatePayload } from '@vue/devtools-api' const MAX_$VM = 10 const $vmQueue = [] -export async function sendComponentTreeData (appRecord: AppRecord, instanceId: string, filter = '', maxDepth: number = null, recursively = false, ctx: BackendContext) { - if (!instanceId || appRecord !== ctx.currentAppRecord) return +export async function sendComponentTreeData(appRecord: AppRecord, instanceId: string, filter = '', maxDepth: number = null, recursively = false, ctx: BackendContext) { + if (!instanceId || appRecord !== ctx.currentAppRecord) { + return + } // Flush will send all components in the tree // So we skip individiual tree updates if ( - instanceId !== '_root' && - ctx.currentAppRecord.backend.options.features.includes(BuiltinBackendFeature.FLUSH) + instanceId !== '_root' + && ctx.currentAppRecord.backend.options.features.includes(BuiltinBackendFeature.FLUSH) ) { return } @@ -25,8 +28,11 @@ export async function sendComponentTreeData (appRecord: AppRecord, instanceId: s treeData: null, notFound: true, }) - } else { - if (filter) filter = filter.toLowerCase() + } + else { + if (filter) { + filter = filter.toLowerCase() + } if (maxDepth == null) { maxDepth = instance === ctx.currentAppRecord.rootInstance ? 2 : 1 } @@ -39,15 +45,18 @@ export async function sendComponentTreeData (appRecord: AppRecord, instanceId: s } } -export async function sendSelectedComponentData (appRecord: AppRecord, instanceId: string, ctx: BackendContext) { - if (!instanceId || appRecord !== ctx.currentAppRecord) return +export async function sendSelectedComponentData(appRecord: AppRecord, instanceId: string, ctx: BackendContext) { + if (!instanceId || appRecord !== ctx.currentAppRecord) { + return + } const instance = getComponentInstance(appRecord, instanceId, ctx) if (!instance) { sendEmptyComponentData(instanceId, ctx) - } else { + } + else { // Expose instance on window if (typeof window !== 'undefined') { - const win = (window as any) + const win = window as any win.$vm = instance // $vm0, $vm1, $vm2, ... @@ -76,20 +85,22 @@ export async function sendSelectedComponentData (appRecord: AppRecord, instanceI } } -export function markSelectedInstance (instanceId: string, ctx: BackendContext) { +export function markSelectedInstance(instanceId: string, ctx: BackendContext) { ctx.currentInspectedComponentId = instanceId ctx.currentAppRecord.lastInspectedComponentId = instanceId } -export function sendEmptyComponentData (instanceId: string, ctx: BackendContext) { +export function sendEmptyComponentData(instanceId: string, ctx: BackendContext) { ctx.bridge.send(BridgeEvents.TO_FRONT_COMPONENT_SELECTED_DATA, { instanceId, data: null, }) } -export async function editComponentState (instanceId: string, dotPath: string, type: string, state: EditStatePayload, ctx: BackendContext) { - if (!instanceId) return +export async function editComponentState(instanceId: string, dotPath: string, type: string, state: EditStatePayload, ctx: BackendContext) { + if (!instanceId) { + return + } const instance = getComponentInstance(ctx.currentAppRecord, instanceId, ctx) if (instance) { if ('value' in state && state.value != null) { @@ -100,14 +111,19 @@ export async function editComponentState (instanceId: string, dotPath: string, t } } -export async function getComponentId (app: App, uid: number, instance: ComponentInstance, ctx: BackendContext) { +export async function getComponentId(app: App, uid: number, instance: ComponentInstance, ctx: BackendContext) { try { - if (instance.__VUE_DEVTOOLS_UID__) return instance.__VUE_DEVTOOLS_UID__ + if (instance.__VUE_DEVTOOLS_UID__) { + return instance.__VUE_DEVTOOLS_UID__ + } const appRecord = await getAppRecord(app, ctx) - if (!appRecord) return null + if (!appRecord) { + return null + } const isRoot = appRecord.rootInstance === instance return `${appRecord.id}:${isRoot ? 'root' : uid}` - } catch (e) { + } + catch (e) { if (SharedData.debugInfo) { console.error(e) } @@ -115,7 +131,7 @@ export async function getComponentId (app: App, uid: number, instance: Component } } -export function getComponentInstance (appRecord: AppRecord, instanceId: string, ctx: BackendContext) { +export function getComponentInstance(appRecord: AppRecord, instanceId: string, _ctx: BackendContext) { if (instanceId === '_root') { instanceId = `${appRecord.id}:root` } @@ -126,15 +142,19 @@ export function getComponentInstance (appRecord: AppRecord, instanceId: string, return instance } -export async function refreshComponentTreeSearch (ctx: BackendContext) { - if (!ctx.currentAppRecord.componentFilter) return +export async function refreshComponentTreeSearch(ctx: BackendContext) { + if (!ctx.currentAppRecord.componentFilter) { + return + } await sendComponentTreeData(ctx.currentAppRecord, '_root', ctx.currentAppRecord.componentFilter, null, false, ctx) } const updateTrackingQueue = createThrottleQueue(500) -export function sendComponentUpdateTracking (instanceId: string, time: number, ctx: BackendContext) { - if (!instanceId) return +export function sendComponentUpdateTracking(instanceId: string, time: number, ctx: BackendContext) { + if (!instanceId) { + return + } updateTrackingQueue.add(instanceId, () => { const payload = { diff --git a/packages/app-backend-core/src/flash.ts b/packages/app-backend-core/src/flash.ts index 77d26bff6..8c9bdf38a 100644 --- a/packages/app-backend-core/src/flash.ts +++ b/packages/app-backend-core/src/flash.ts @@ -1,7 +1,7 @@ -import { DevtoolsBackend } from '@vue-devtools/app-backend-api' -import { ComponentInstance } from '@vue/devtools-api' +import type { DevtoolsBackend } from '@vue-devtools/app-backend-api' +import type { ComponentInstance } from '@vue/devtools-api' -export async function flashComponent (instance: ComponentInstance, backend: DevtoolsBackend) { +export async function flashComponent(instance: ComponentInstance, backend: DevtoolsBackend) { const bounds = await backend.api.getComponentBounds(instance) if (bounds) { let overlay: HTMLDivElement = instance.__VUE_DEVTOOLS_FLASH @@ -18,10 +18,10 @@ export async function flashComponent (instance: ComponentInstance, backend: Devt } overlay.style.opacity = '1' overlay.style.transition = null - overlay.style.width = Math.round(bounds.width) + 'px' - overlay.style.height = Math.round(bounds.height) + 'px' - overlay.style.left = Math.round(bounds.left) + 'px' - overlay.style.top = Math.round(bounds.top) + 'px' + overlay.style.width = `${Math.round(bounds.width)}px` + overlay.style.height = `${Math.round(bounds.height)}px` + overlay.style.left = `${Math.round(bounds.left)}px` + overlay.style.top = `${Math.round(bounds.top)}px` requestAnimationFrame(() => { overlay.style.transition = 'opacity 1s' overlay.style.opacity = '0' diff --git a/packages/app-backend-core/src/global-hook.ts b/packages/app-backend-core/src/global-hook.ts index be2c638c5..bbdcd3eaa 100644 --- a/packages/app-backend-core/src/global-hook.ts +++ b/packages/app-backend-core/src/global-hook.ts @@ -1,4 +1,4 @@ -import { DevtoolsHook } from '@vue-devtools/app-backend-api' +import type { DevtoolsHook } from '@vue-devtools/app-backend-api' import { target } from '@vue-devtools/shared-utils' // hook should have been injected before this executes. diff --git a/packages/app-backend-core/src/highlighter.ts b/packages/app-backend-core/src/highlighter.ts index 76d77aeed..6a2cb9247 100644 --- a/packages/app-backend-core/src/highlighter.ts +++ b/packages/app-backend-core/src/highlighter.ts @@ -1,14 +1,16 @@ import { isBrowser } from '@vue-devtools/shared-utils' -import { BackendContext, DevtoolsBackend } from '@vue-devtools/app-backend-api' -import { ComponentBounds, ComponentInstance } from '@vue/devtools-api' +import type { BackendContext, DevtoolsBackend } from '@vue-devtools/app-backend-api' +import type { ComponentBounds, ComponentInstance } from '@vue/devtools-api' import { JobQueue } from './util/queue' let overlay: HTMLDivElement let overlayContent: HTMLDivElement let currentInstance -function createOverlay () { - if (overlay || !isBrowser) return +function createOverlay() { + if (overlay || !isBrowser) { + return + } overlay = document.createElement('div') overlay.style.backgroundColor = 'rgba(65, 184, 131, 0.35)' overlay.style.position = 'fixed' @@ -34,9 +36,11 @@ function createOverlay () { // This prevents "sticky" highlights that are not removed because highlight is async const jobQueue = new JobQueue() -export async function highlight (instance: ComponentInstance, backend: DevtoolsBackend, ctx: BackendContext) { +export async function highlight(instance: ComponentInstance, backend: DevtoolsBackend, ctx: BackendContext) { await jobQueue.queue('highlight', async () => { - if (!instance) return + if (!instance) { + return + } const bounds = await backend.api.getComponentBounds(instance) if (bounds) { @@ -46,14 +50,14 @@ export async function highlight (instance: ComponentInstance, backend: DevtoolsB const name = (await backend.api.getComponentName(instance)) || 'Anonymous' const pre = document.createElement('span') pre.style.opacity = '0.6' - pre.innerText = '<' + pre.textContent = '<' const text = document.createElement('span') text.style.fontWeight = 'bold' text.style.color = '#09ab56' - text.innerText = name + text.textContent = name const post = document.createElement('span') post.style.opacity = '0.6' - post.innerText = '>' + post.textContent = '>' // Size const size = document.createElement('span') @@ -62,7 +66,7 @@ export async function highlight (instance: ComponentInstance, backend: DevtoolsB size.appendChild(document.createTextNode((Math.round(bounds.width * 100) / 100).toString())) const multiply = document.createElement('span') multiply.style.marginLeft = multiply.style.marginRight = '2px' - multiply.innerText = '×' + multiply.textContent = '×' size.appendChild(multiply) size.appendChild(document.createTextNode((Math.round(bounds.height * 100) / 100).toString())) @@ -75,7 +79,7 @@ export async function highlight (instance: ComponentInstance, backend: DevtoolsB }) } -export async function unHighlight () { +export async function unHighlight() { await jobQueue.queue('unHighlight', async () => { overlay?.parentNode?.removeChild(overlay) overlayContent?.parentNode?.removeChild(overlayContent) @@ -85,8 +89,10 @@ export async function unHighlight () { }) } -function showOverlay (bounds: ComponentBounds, children: Node[] = null) { - if (!isBrowser || !children.length) return +function showOverlay(bounds: ComponentBounds, children: Node[] = null) { + if (!isBrowser || !children.length) { + return + } positionOverlay(bounds) document.body.appendChild(overlay) @@ -98,21 +104,22 @@ function showOverlay (bounds: ComponentBounds, children: Node[] = null) { positionOverlayContent(bounds) } -function positionOverlay ({ width = 0, height = 0, top = 0, left = 0 }) { - overlay.style.width = Math.round(width) + 'px' - overlay.style.height = Math.round(height) + 'px' - overlay.style.left = Math.round(left) + 'px' - overlay.style.top = Math.round(top) + 'px' +function positionOverlay({ width = 0, height = 0, top = 0, left = 0 }) { + overlay.style.width = `${Math.round(width)}px` + overlay.style.height = `${Math.round(height)}px` + overlay.style.left = `${Math.round(left)}px` + overlay.style.top = `${Math.round(top)}px` } -function positionOverlayContent ({ height = 0, top = 0, left = 0 }) { +function positionOverlayContent({ height = 0, top = 0, left = 0 }) { // Content position (prevents overflow) const contentWidth = overlayContent.offsetWidth const contentHeight = overlayContent.offsetHeight let contentLeft = left if (contentLeft < 0) { contentLeft = 0 - } else if (contentLeft + contentWidth > window.innerWidth) { + } + else if (contentLeft + contentWidth > window.innerWidth) { contentLeft = window.innerWidth - contentWidth } let contentTop = top - contentHeight - 2 @@ -121,14 +128,15 @@ function positionOverlayContent ({ height = 0, top = 0, left = 0 }) { } if (contentTop < 0) { contentTop = 0 - } else if (contentTop + contentHeight > window.innerHeight) { + } + else if (contentTop + contentHeight > window.innerHeight) { contentTop = window.innerHeight - contentHeight } - overlayContent.style.left = ~~contentLeft + 'px' - overlayContent.style.top = ~~contentTop + 'px' + overlayContent.style.left = `${~~contentLeft}px` + overlayContent.style.top = `${~~contentTop}px` } -async function updateOverlay (backend: DevtoolsBackend, ctx: BackendContext) { +async function updateOverlay(backend: DevtoolsBackend, _ctx: BackendContext) { if (currentInstance) { const bounds = await backend.api.getComponentBounds(currentInstance) if (bounds) { @@ -146,7 +154,7 @@ async function updateOverlay (backend: DevtoolsBackend, ctx: BackendContext) { let updateTimer -function startUpdateTimer (backend: DevtoolsBackend, ctx: BackendContext) { +function startUpdateTimer(backend: DevtoolsBackend, ctx: BackendContext) { stopUpdateTimer() updateTimer = setInterval(() => { jobQueue.queue('updateOverlay', async () => { @@ -155,6 +163,6 @@ function startUpdateTimer (backend: DevtoolsBackend, ctx: BackendContext) { }, 1000 / 30) // 30fps } -function stopUpdateTimer () { +function stopUpdateTimer() { clearInterval(updateTimer) } diff --git a/packages/app-backend-core/src/hook.ts b/packages/app-backend-core/src/hook.ts index 105c7548a..c13558a6f 100644 --- a/packages/app-backend-core/src/hook.ts +++ b/packages/app-backend-core/src/hook.ts @@ -8,35 +8,41 @@ * * @param {Window|global} target */ -export function installHook (target, isIframe = false) { +export function installHook(target, isIframe = false) { const devtoolsVersion = '6.0' let listeners = {} - function injectIframeHook (iframe) { - if ((iframe as any).__vdevtools__injected) return + function injectIframeHook(iframe) { + if ((iframe as any).__vdevtools__injected) { + return + } try { (iframe as any).__vdevtools__injected = true const inject = () => { try { (iframe.contentWindow as any).__VUE_DEVTOOLS_IFRAME__ = iframe const script = iframe.contentDocument.createElement('script') - script.textContent = ';(' + installHook.toString() + ')(window, true)' + script.textContent = `;(${installHook.toString()})(window, true)` iframe.contentDocument.documentElement.appendChild(script) script.parentNode.removeChild(script) - } catch (e) { + } + catch (e) { // Ignore } } inject() iframe.addEventListener('load', () => inject()) - } catch (e) { + } + catch (e) { // Ignore } } let iframeChecks = 0 - function injectToIframes () { - if (typeof window === 'undefined') return + function injectToIframes() { + if (typeof window === 'undefined') { + return + } const iframes = document.querySelectorAll('iframe:not([data-vue-devtools-ignore])') for (const iframe of iframes) { @@ -62,15 +68,17 @@ export function installHook (target, isIframe = false) { let hook if (isIframe) { - const sendToParent = cb => { + const sendToParent = (cb) => { try { const hook = (window.parent as any).__VUE_DEVTOOLS_GLOBAL_HOOK__ if (hook) { return cb(hook) - } else { + } + else { console.warn('[Vue Devtools] No hook in parent window') } - } catch (e) { + } + catch (e) { console.warn('[Vue Devtools] Failed to send message to parent window', e) } } @@ -78,36 +86,41 @@ export function installHook (target, isIframe = false) { hook = { devtoolsVersion, // eslint-disable-next-line accessor-pairs - set Vue (value) { - sendToParent(hook => { hook.Vue = value }) + set Vue(value) { + sendToParent((hook) => { + hook.Vue = value + }) }, // eslint-disable-next-line accessor-pairs - set enabled (value) { - sendToParent(hook => { hook.enabled = value }) + set enabled(value) { + sendToParent((hook) => { + hook.enabled = value + }) }, - on (event, fn) { + on(event, fn) { sendToParent(hook => hook.on(event, fn)) }, - once (event, fn) { + once(event, fn) { sendToParent(hook => hook.once(event, fn)) }, - off (event, fn) { + off(event, fn) { sendToParent(hook => hook.off(event, fn)) }, - emit (event, ...args) { + emit(event, ...args) { sendToParent(hook => hook.emit(event, ...args)) }, - cleanupBuffer (matchArg) { + cleanupBuffer(matchArg) { return sendToParent(hook => hook.cleanupBuffer(matchArg)) ?? false }, } - } else { + } + else { hook = { devtoolsVersion, Vue: null, @@ -121,7 +134,7 @@ export function installHook (target, isIframe = false) { flushStoreModules: null, apps: [], - _replayBuffer (event) { + _replayBuffer(event) { const buffer = this._buffer this._buffer = [] this._bufferMap.clear() @@ -136,17 +149,18 @@ export function installHook (target, isIframe = false) { } }, - on (event, fn) { - const $event = '$' + event + on(event, fn) { + const $event = `$${event}` if (listeners[$event]) { listeners[$event].push(fn) - } else { + } + else { listeners[$event] = [fn] this._replayBuffer(event) } }, - once (event, fn) { + once(event, fn) { const on = (...args) => { this.off(event, on) return fn.apply(this, args) @@ -154,16 +168,18 @@ export function installHook (target, isIframe = false) { this.on(event, on) }, - off (event, fn) { - event = '$' + event + off(event, fn) { + event = `$${event}` if (!arguments.length) { listeners = {} - } else { + } + else { const cbs = listeners[event] if (cbs) { if (!fn) { listeners[event] = null - } else { + } + else { for (let i = 0, l = cbs.length; i < l; i++) { const cb = cbs[i] if (cb === fn || cb.fn === fn) { @@ -176,8 +192,8 @@ export function installHook (target, isIframe = false) { } }, - emit (event, ...args) { - const $event = '$' + event + emit(event, ...args) { + const $event = `$${event}` let cbs = listeners[$event] if (cbs) { cbs = cbs.slice() @@ -185,17 +201,19 @@ export function installHook (target, isIframe = false) { try { const result = cbs[i].apply(this, args) if (typeof result?.catch === 'function') { - result.catch(e => { + result.catch((e) => { console.error(`[Hook] Error in async event handler for ${event} with args:`, args) console.error(e) }) } - } catch (e) { + } + catch (e) { console.error(`[Hook] Error in event handler for ${event} with args:`, args) console.error(e) } } - } else { + } + else { const buffered = [Date.now(), event, ...args] this._buffer.push(buffered) @@ -213,7 +231,7 @@ export function installHook (target, isIframe = false) { * Remove buffered events with any argument that is equal to the given value. * @param matchArg Given value to match. */ - cleanupBuffer (matchArg) { + cleanupBuffer(matchArg) { const inBuffer = this._bufferMap.has(matchArg) if (inBuffer) { // Mark event for removal @@ -222,7 +240,7 @@ export function installHook (target, isIframe = false) { return inBuffer }, - _cleanupBuffer () { + _cleanupBuffer() { const now = Date.now() // Clear buffer events that are older than 10 seconds or marked for removal this._buffer = this._buffer.filter(args => !this._bufferToRemove.has(args) && now - args[0] < 10_000) @@ -235,7 +253,7 @@ export function installHook (target, isIframe = false) { hook._cleanupBuffer() }, 10_000) - hook.once('init', Vue => { + hook.once('init', (Vue) => { hook.Vue = Vue if (Vue) { @@ -256,11 +274,11 @@ export function installHook (target, isIframe = false) { hook.emit('app:add', appRecord) }) - hook.once('vuex:init', store => { + hook.once('vuex:init', (store) => { hook.store = store hook.initialState = clone(store.state) const origReplaceState = store.replaceState.bind(store) - store.replaceState = state => { + store.replaceState = (state) => { hook.initialState = clone(state) origReplaceState(state) } @@ -270,7 +288,9 @@ export function installHook (target, isIframe = false) { hook.storeModules = [] origRegister = store.registerModule.bind(store) store.registerModule = (path, module, options) => { - if (typeof path === 'string') path = [path] + if (typeof path === 'string') { + path = [path] + } hook.storeModules.push({ path, module, options }) origRegister(path, module, options) if (process.env.NODE_ENV !== 'production') { @@ -280,10 +300,14 @@ export function installHook (target, isIframe = false) { } origUnregister = store.unregisterModule.bind(store) store.unregisterModule = (path) => { - if (typeof path === 'string') path = [path] + if (typeof path === 'string') { + path = [path] + } const key = path.join('/') const index = hook.storeModules.findIndex(m => m.path.join('/') === key) - if (index !== -1) hook.storeModules.splice(index, 1) + if (index !== -1) { + hook.storeModules.splice(index, 1) + } origUnregister(path) if (process.env.NODE_ENV !== 'production') { // eslint-disable-next-line no-console @@ -303,7 +327,7 @@ export function installHook (target, isIframe = false) { } Object.defineProperty(target, '__VUE_DEVTOOLS_GLOBAL_HOOK__', { - get () { + get() { return hook }, }) @@ -313,7 +337,8 @@ export function installHook (target, isIframe = false) { try { target.__VUE_DEVTOOLS_HOOK_REPLAY__.forEach(cb => cb(hook)) target.__VUE_DEVTOOLS_HOOK_REPLAY__ = [] - } catch (e) { + } + catch (e) { console.error('[vue-devtools] Error during hook replay', e) } } @@ -338,7 +363,7 @@ export function installHook (target, isIframe = false) { /** * @enum * - * @const {Object} SUPPORTS + * @const {object} SUPPORTS * * @property {boolean} SYMBOL_PROPERTIES are symbol properties supported * @property {boolean} WEAKSET is WeakSet supported @@ -362,8 +387,8 @@ export function installHook (target, isIframe = false) { } const object = create({ - add: (value) => object._values.push(value), - has: (value) => !!~object._values.indexOf(value), + add: value => object._values.push(value), + has: value => !!~object._values.indexOf(value), }) object._values = [] @@ -386,7 +411,7 @@ export function installHook (target, isIframe = false) { return create(null) } - // eslint-disable-next-line no-proto + // eslint-disable-next-line no-proto, no-restricted-properties const prototype = object.__proto__ || getPrototypeOf(object) if (object.constructor === realm.Object) { @@ -396,7 +421,8 @@ export function installHook (target, isIframe = false) { if (~toStringFunction.call(object.constructor).indexOf('[native code]')) { try { return new object.constructor() - } catch (e) { + } + catch (e) { // Error } } @@ -530,7 +556,9 @@ export function installHook (target, isIframe = false) { const { isArray } = Array const GLOBAL_THIS = (() => { + // eslint-disable-next-line no-restricted-globals if (typeof self !== 'undefined') { + // eslint-disable-next-line no-restricted-globals return self } @@ -538,8 +566,8 @@ export function installHook (target, isIframe = false) { return window } - if (typeof global !== 'undefined') { - return global + if (typeof globalThis !== 'undefined') { + return globalThis } if (console && console.error) { @@ -566,7 +594,7 @@ export function installHook (target, isIframe = false) { * @param [options.realm] the realm (this) object the object is copied from * @returns the copied object */ - function clone (object, options = null) { + function clone(object, options = null) { // manually coalesced instead of default parameters for performance const isStrict = !!(options && options.isStrict) const realm = (options && options.realm) || GLOBAL_THIS @@ -696,13 +724,13 @@ export function installHook (target, isIframe = false) { // if the object cannot / should not be cloned, don't if ( // promise-like - (hasOwnProperty.call(object, 'then') && typeof object.then === 'function') || + (hasOwnProperty.call(object, 'then') && typeof object.then === 'function') // errors - object instanceof Error || + || object instanceof Error // weakmaps - (realm.WeakMap && object instanceof realm.WeakMap) || + || (realm.WeakMap && object instanceof realm.WeakMap) // weaksets - (realm.WeakSet && object instanceof realm.WeakSet) + || (realm.WeakSet && object instanceof realm.WeakSet) ) { return object } diff --git a/packages/app-backend-core/src/index.ts b/packages/app-backend-core/src/index.ts index c83eea842..069d15a91 100644 --- a/packages/app-backend-core/src/index.ts +++ b/packages/app-backend-core/src/index.ts @@ -1,57 +1,63 @@ -import { - createBackendContext, +import type { + AppRecord, BackendContext, Plugin, - BuiltinBackendFeature, - AppRecord, } from '@vue-devtools/app-backend-api' import { + BuiltinBackendFeature, + createBackendContext, +} from '@vue-devtools/app-backend-api' +import type { Bridge, - HookEvents, +} from '@vue-devtools/shared-utils' +import { BridgeEvents, + BridgeSubscriptions, BuiltinTabs, + HookEvents, + SharedData, + createThrottleQueue, + getPluginSettings, initSharedData, - BridgeSubscriptions, + isBrowser, parse, + raf, revive, target, - getPluginSettings, - SharedData, - isBrowser, - raf, - createThrottleQueue, } from '@vue-devtools/shared-utils' import debounce from 'lodash/debounce' -import throttle from 'lodash/throttle' +import type { CustomInspectorOptions, PluginDescriptor, SetupFunction, TimelineEventOptions, TimelineLayerOptions } from '@vue/devtools-api' +import { Hooks, now } from '@vue/devtools-api' import { hook } from './global-hook' -import { subscribe, unsubscribe, isSubscribed } from './util/subscriptions' +import { isSubscribed, subscribe, unsubscribe } from './util/subscriptions' import { highlight, unHighlight } from './highlighter' -import { setupTimeline, sendTimelineLayers, addTimelineEvent, clearTimeline, sendTimelineEventData, sendTimelineLayerEvents } from './timeline' +import { addTimelineEvent, clearTimeline, sendTimelineEventData, sendTimelineLayerEvents, sendTimelineLayers, setupTimeline } from './timeline' import ComponentPicker from './component-pick' import { - sendComponentTreeData, - sendSelectedComponentData, - sendEmptyComponentData, - getComponentId, editComponentState, + getComponentId, getComponentInstance, refreshComponentTreeSearch, + sendComponentTreeData, sendComponentUpdateTracking, + sendEmptyComponentData, + sendSelectedComponentData, } from './component' -import { addQueuedPlugins, addPlugin, sendPluginList, addPreviouslyRegisteredPlugins } from './plugin' -import { PluginDescriptor, SetupFunction, TimelineLayerOptions, TimelineEventOptions, CustomInspectorOptions, Hooks, now } from '@vue/devtools-api' -import { registerApp, selectApp, waitForAppsRegistration, sendApps, _legacy_getAndRegisterApps, getAppRecord, removeApp } from './app' -import { sendInspectorTree, getInspector, getInspectorWithAppId, sendInspectorState, editInspectorState, sendCustomInspectors, selectInspectorNode } from './inspector' +import { addPlugin, addPreviouslyRegisteredPlugins, addQueuedPlugins, sendPluginList } from './plugin' +import { _legacy_getAndRegisterApps, getAppRecord, registerApp, removeApp, selectApp, sendApps, waitForAppsRegistration } from './app' +import { editInspectorState, getInspector, getInspectorWithAppId, selectInspectorNode, sendCustomInspectors, sendInspectorState, sendInspectorTree } from './inspector' import { showScreenshot } from './timeline-screenshot' import { performanceMarkEnd, performanceMarkStart } from './perf' import { initOnPageConfig } from './page-config' -import { sendTimelineMarkers, addTimelineMarker } from './timeline-marker' +import { addTimelineMarker, sendTimelineMarkers } from './timeline-marker' import { flashComponent } from './flash.js' let ctx: BackendContext = target.__vdevtools_ctx ?? null let connected = target.__vdevtools_connected ?? false -export async function initBackend (bridge: Bridge) { +let pageTitleObserver: MutationObserver + +export async function initBackend(bridge: Bridge) { await initSharedData({ bridge, persist: false, @@ -79,19 +85,20 @@ export async function initBackend (bridge: Bridge) { SharedData.legacyApps = true }) - hook.on(HookEvents.APP_ADD, async app => { + hook.on(HookEvents.APP_ADD, async (app) => { await registerApp(app, ctx) connect() }) // Add apps that already sent init if (hook.apps.length) { - hook.apps.forEach(app => { + hook.apps.forEach((app) => { registerApp(app, ctx) connect() }) } - } else { + } + else { // Reconnect ctx.bridge = bridge connectBridge() @@ -99,7 +106,7 @@ export async function initBackend (bridge: Bridge) { } } -async function connect () { +async function connect() { if (connected) { return } @@ -113,7 +120,7 @@ async function connect () { // Apps - hook.on(HookEvents.APP_UNMOUNT, async app => { + hook.on(HookEvents.APP_UNMOUNT, async (app) => { await removeApp(app, ctx) }) @@ -123,7 +130,9 @@ async function connect () { hook.on(HookEvents.COMPONENT_UPDATED, async (app, uid, parentUid, component) => { try { - if (!app || (typeof uid !== 'number' && !uid) || !component) return + if (!app || (typeof uid !== 'number' && !uid) || !component) { + return + } const now = Date.now() let id: string @@ -131,28 +140,22 @@ async function connect () { if (app && uid != null) { id = await getComponentId(app, uid, component, ctx) appRecord = await getAppRecord(app, ctx) - } else { + } + else { id = ctx.currentInspectedComponentId appRecord = ctx.currentAppRecord } throttleQueue.add(`update:${id}`, async () => { try { - const time = performance.now() - - const trackUpdateNow = performance.now() if (SharedData.trackUpdates) { sendComponentUpdateTracking(id, now, ctx) } - const trackUpdateTime = performance.now() - trackUpdateNow - const flashNow = performance.now() if (SharedData.flashUpdates) { await flashComponent(component, appRecord.backend) } - const flashTime = performance.now() - flashNow - const sendUpdateNow = performance.now() // Update component inspector if (ctx.currentInspectedComponentId === id) { await sendSelectedComponentData(appRecord, ctx.currentInspectedComponentId, ctx) @@ -162,15 +165,15 @@ async function connect () { if (isSubscribed(BridgeSubscriptions.COMPONENT_TREE, id)) { await sendComponentTreeData(appRecord, id, appRecord.componentFilter, 0, false, ctx) } - const sendUpdateTime = performance.now() - sendUpdateNow - // console.log('COMPONENT_UPDATED', id, Math.round(performance.now() - time) + 'ms', { trackUpdateTime, flashTime, sendUpdateTime, updatedData: ctx.currentInspectedComponentId === id }) - } catch (e) { + } + catch (e) { if (SharedData.debugInfo) { console.error(e) } } }) - } catch (e) { + } + catch (e) { if (SharedData.debugInfo) { console.error(e) } @@ -179,13 +182,14 @@ async function connect () { hook.on(HookEvents.COMPONENT_ADDED, async (app, uid, parentUid, component) => { try { - if (!app || (typeof uid !== 'number' && !uid) || !component) return + if (!app || (typeof uid !== 'number' && !uid) || !component) { + return + } const now = Date.now() const id = await getComponentId(app, uid, component, ctx) throttleQueue.add(`add:${id}`, async () => { try { - const time = performance.now() const appRecord = await getAppRecord(app, ctx) if (component) { if (component.__VUE_DEVTOOLS_UID__ == null) { @@ -230,14 +234,15 @@ async function connect () { } await refreshComponentTreeSearch(ctx) - // console.log('COMPONENT_ADDED', id, Math.round(performance.now() - time) + 'ms') - } catch (e) { + } + catch (e) { if (SharedData.debugInfo) { console.error(e) } } }) - } catch (e) { + } + catch (e) { if (SharedData.debugInfo) { console.error(e) } @@ -246,12 +251,13 @@ async function connect () { hook.on(HookEvents.COMPONENT_REMOVED, async (app, uid, parentUid, component) => { try { - if (!app || (typeof uid !== 'number' && !uid) || !component) return + if (!app || (typeof uid !== 'number' && !uid) || !component) { + return + } const id = await getComponentId(app, uid, component, ctx) throttleQueue.add(`remove:${id}`, async () => { try { - const time = performance.now() const appRecord = await getAppRecord(app, ctx) if (parentUid != null && appRecord) { const parentInstances = await appRecord.backend.api.walkComponentParents(component) @@ -265,7 +271,8 @@ async function connect () { if (appRecord) { sendComponentTreeData(appRecord, parentId, appRecord.componentFilter, null, false, ctx) } - } catch (e) { + } + catch (e) { if (SharedData.debugInfo) { console.error(e) } @@ -284,14 +291,15 @@ async function connect () { } await refreshComponentTreeSearch(ctx) - // console.log('COMPONENT_REMOVED', id, Math.round(performance.now() - time) + 'ms') - } catch (e) { + } + catch (e) { if (SharedData.debugInfo) { console.error(e) } } }) - } catch (e) { + } + catch (e) { if (SharedData.debugInfo) { console.error(e) } @@ -318,7 +326,7 @@ async function connect () { // Highlighter - hook.on(HookEvents.COMPONENT_HIGHLIGHT, instanceId => { + hook.on(HookEvents.COMPONENT_HIGHLIGHT, (instanceId) => { highlight(ctx.currentAppRecord.instanceMap.get(instanceId), ctx.currentAppRecord.backend, ctx) }) @@ -367,7 +375,8 @@ async function connect () { const inspector = getInspector(inspectorId, plugin.descriptor.app, ctx) if (inspector) { await sendInspectorTree(inspector, ctx) - } else if (SharedData.debugInfo) { + } + else if (SharedData.debugInfo) { console.warn(`Inspector ${inspectorId} not found`) } }) @@ -376,7 +385,8 @@ async function connect () { const inspector = getInspector(inspectorId, plugin.descriptor.app, ctx) if (inspector) { await sendInspectorState(inspector, ctx) - } else if (SharedData.debugInfo) { + } + else if (SharedData.debugInfo) { console.warn(`Inspector ${inspectorId} not found`) } }) @@ -385,7 +395,8 @@ async function connect () { const inspector = getInspector(inspectorId, plugin.descriptor.app, ctx) if (inspector) { await selectInspectorNode(inspector, nodeId, ctx) - } else if (SharedData.debugInfo) { + } + else if (SharedData.debugInfo) { console.warn(`Inspector ${inspectorId} not found`) } }) @@ -394,13 +405,15 @@ async function connect () { try { await addPreviouslyRegisteredPlugins(ctx) - } catch (e) { + } + catch (e) { console.error(`Error adding previously registered plugins:`) console.error(e) } try { await addQueuedPlugins(ctx) - } catch (e) { + } + catch (e) { console.error(`Error adding queued plugins:`) console.error(e) } @@ -435,13 +448,14 @@ async function connect () { color: 0x41B883, all: true, }, ctx) - } catch (e) { + } + catch (e) { console.error(`Error while adding devtools connected timeline marker:`) console.error(e) } } -function connectBridge () { +function connectBridge() { // Subscriptions ctx.bridge.on(BridgeEvents.TO_BACK_SUBSCRIBE, ({ type, key }) => { @@ -454,7 +468,7 @@ function connectBridge () { // Tabs - ctx.bridge.on(BridgeEvents.TO_BACK_TAB_SWITCH, async tab => { + ctx.bridge.on(BridgeEvents.TO_BACK_TAB_SWITCH, async (tab) => { ctx.currentTab = tab await unHighlight() }) @@ -465,12 +479,15 @@ function connectBridge () { await sendApps(ctx) }) - ctx.bridge.on(BridgeEvents.TO_BACK_APP_SELECT, async id => { - if (id == null) return + ctx.bridge.on(BridgeEvents.TO_BACK_APP_SELECT, async (id) => { + if (id == null) { + return + } const record = ctx.appRecords.find(r => r.id === id) if (record) { await selectApp(record, ctx) - } else if (SharedData.debugInfo) { + } + else if (SharedData.debugInfo) { console.warn(`App with id ${id} not found`) } }) @@ -502,7 +519,6 @@ function connectBridge () { if (instance) { const [el] = await ctx.currentAppRecord.backend.api.getComponentRootElements(instance) if (el) { - // @ts-ignore target.__VUE_DEVTOOLS_INSPECT_TARGET__ = el ctx.bridge.send(BridgeEvents.TO_FRONT_COMPONENT_INSPECT_DOM, null) } @@ -510,7 +526,9 @@ function connectBridge () { }) ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_SCROLL_TO, async ({ instanceId }) => { - if (!isBrowser) return + if (!isBrowser) { + return + } const instance = getComponentInstance(ctx.currentAppRecord, instanceId, ctx) if (instance) { const [el] = await ctx.currentAppRecord.backend.api.getComponentRootElements(instance) @@ -521,7 +539,8 @@ function connectBridge () { block: 'center', inline: 'center', }) - } else { + } + else { // Handle nodes that don't implement scrollIntoView const bounds = await ctx.currentAppRecord.backend.api.getComponentBounds(instance) const scrollTarget = document.createElement('div') @@ -549,7 +568,9 @@ function connectBridge () { }) ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_RENDER_CODE, async ({ instanceId }) => { - if (!isBrowser) return + if (!isBrowser) { + return + } const instance = getComponentInstance(ctx.currentAppRecord, instanceId, ctx) if (instance) { const { code } = await ctx.currentAppRecord.backend.api.getComponentRenderCode(instance) @@ -566,17 +587,19 @@ function connectBridge () { if (action) { try { await action() - } catch (e) { + } + catch (e) { console.error(e) } - } else { + } + else { console.warn(`Couldn't revive action ${actionIndex} from`, value) } }) // Highlighter - ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_MOUSE_OVER, async instanceId => { + ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_MOUSE_OVER, async (instanceId) => { await highlight(ctx.currentAppRecord.instanceMap.get(instanceId), ctx.currentAppRecord.backend, ctx) }) @@ -633,7 +656,8 @@ function connectBridge () { if (inspector) { inspector.treeFilter = treeFilter sendInspectorTree(inspector, ctx) - } else if (SharedData.debugInfo) { + } + else if (SharedData.debugInfo) { console.warn(`Inspector ${inspectorId} not found`) } }) @@ -643,7 +667,8 @@ function connectBridge () { if (inspector) { inspector.selectedNodeId = nodeId sendInspectorState(inspector, ctx) - } else if (SharedData.debugInfo) { + } + else if (SharedData.debugInfo) { console.warn(`Inspector ${inspectorId} not found`) } }) @@ -654,7 +679,8 @@ function connectBridge () { await editInspectorState(inspector, nodeId, path, type, payload, ctx) inspector.selectedNodeId = nodeId await sendInspectorState(inspector, ctx) - } else if (SharedData.debugInfo) { + } + else if (SharedData.debugInfo) { console.warn(`Inspector ${inspectorId} not found`) } }) @@ -665,12 +691,14 @@ function connectBridge () { const action = inspector[actionType ?? 'actions'][actionIndex] try { await action.action(...(args ?? [])) - } catch (e) { + } + catch (e) { if (SharedData.debugInfo) { console.error(e) } } - } else if (SharedData.debugInfo) { + } + else if (SharedData.debugInfo) { console.warn(`Inspector ${inspectorId} not found`) } }) @@ -681,7 +709,8 @@ function connectBridge () { let value = payload.value if (payload.serialized) { value = parse(value, payload.revive) - } else if (payload.revive) { + } + else if (payload.revive) { value = revive(value) } // eslint-disable-next-line no-console @@ -716,10 +745,8 @@ function connectBridge () { } pageTitleObserver = new MutationObserver((mutations) => { const title = mutations[0].target as HTMLTitleElement - ctx.bridge.send(BridgeEvents.TO_FRONT_TITLE, { title: title.innerText }) + ctx.bridge.send(BridgeEvents.TO_FRONT_TITLE, { title: title.textContent }) }) pageTitleObserver.observe(titleEl, { subtree: true, characterData: true, childList: true }) } } - -let pageTitleObserver: MutationObserver diff --git a/packages/app-backend-core/src/inspector.ts b/packages/app-backend-core/src/inspector.ts index 501c3842e..67e79a383 100644 --- a/packages/app-backend-core/src/inspector.ts +++ b/packages/app-backend-core/src/inspector.ts @@ -1,12 +1,12 @@ -import { App } from '@vue/devtools-api' -import { BackendContext, CustomInspector } from '@vue-devtools/app-backend-api' +import type { App } from '@vue/devtools-api' +import type { BackendContext, CustomInspector } from '@vue-devtools/app-backend-api' import { BridgeEvents, parse, stringify } from '@vue-devtools/shared-utils' -export function getInspector (inspectorId: string, app: App, ctx: BackendContext) { +export function getInspector(inspectorId: string, app: App, ctx: BackendContext) { return ctx.customInspectors.find(i => i.id === inspectorId && i.appRecord.options.app === app) } -export async function getInspectorWithAppId (inspectorId: string, appId: string, ctx: BackendContext): Promise { +export async function getInspectorWithAppId(inspectorId: string, appId: string, ctx: BackendContext): Promise { for (const i of ctx.customInspectors) { if (i.id === inspectorId && i.appRecord.id === appId) { return i @@ -15,7 +15,7 @@ export async function getInspectorWithAppId (inspectorId: string, appId: string, return null } -export async function sendInspectorTree (inspector: CustomInspector, ctx: BackendContext) { +export async function sendInspectorTree(inspector: CustomInspector, ctx: BackendContext) { const rootNodes = await inspector.appRecord.backend.api.getInspectorTree(inspector.id, inspector.appRecord.options.app, inspector.treeFilter) ctx.bridge.send(BridgeEvents.TO_FRONT_CUSTOM_INSPECTOR_TREE, { appId: inspector.appRecord.id, @@ -24,7 +24,7 @@ export async function sendInspectorTree (inspector: CustomInspector, ctx: Backen }) } -export async function sendInspectorState (inspector: CustomInspector, ctx: BackendContext) { +export async function sendInspectorState(inspector: CustomInspector, ctx: BackendContext) { const state = inspector.selectedNodeId ? await inspector.appRecord.backend.api.getInspectorState(inspector.id, inspector.appRecord.options.app, inspector.selectedNodeId) : null ctx.bridge.send(BridgeEvents.TO_FRONT_CUSTOM_INSPECTOR_STATE, { appId: inspector.appRecord.id, @@ -33,14 +33,14 @@ export async function sendInspectorState (inspector: CustomInspector, ctx: Backe }) } -export async function editInspectorState (inspector: CustomInspector, nodeId: string, dotPath: string, type: string, state: any, ctx: BackendContext) { +export async function editInspectorState(inspector: CustomInspector, nodeId: string, dotPath: string, type: string, state: any, _ctx: BackendContext) { await inspector.appRecord.backend.api.editInspectorState(inspector.id, inspector.appRecord.options.app, nodeId, dotPath, type, { ...state, value: state.value != null ? parse(state.value, true) : state.value, }) } -export async function sendCustomInspectors (ctx: BackendContext) { +export async function sendCustomInspectors(ctx: BackendContext) { const inspectors = [] for (const i of ctx.customInspectors) { inspectors.push({ @@ -67,7 +67,7 @@ export async function sendCustomInspectors (ctx: BackendContext) { }) } -export async function selectInspectorNode (inspector: CustomInspector, nodeId: string, ctx: BackendContext) { +export async function selectInspectorNode(inspector: CustomInspector, nodeId: string, ctx: BackendContext) { ctx.bridge.send(BridgeEvents.TO_FRONT_CUSTOM_INSPECTOR_SELECT_NODE, { appId: inspector.appRecord.id, inspectorId: inspector.id, diff --git a/packages/app-backend-core/src/legacy/scan.ts b/packages/app-backend-core/src/legacy/scan.ts index bdb73a7fc..7d4428744 100644 --- a/packages/app-backend-core/src/legacy/scan.ts +++ b/packages/app-backend-core/src/legacy/scan.ts @@ -6,16 +6,15 @@ const rootInstances = [] /** * Scan the page for root level Vue instances. */ -export function scan () { +export function scan() { rootInstances.length = 0 let inFragment = false let currentFragment = null - // eslint-disable-next-line no-inner-declarations - function processInstance (instance) { + function processInstance(instance) { if (instance) { - if (rootInstances.indexOf(instance.$root) === -1) { + if (!rootInstances.includes(instance.$root)) { instance = instance.$root } if (instance._isFragment) { @@ -37,8 +36,8 @@ export function scan () { } if (isBrowser) { - const walkDocument = document => { - walk(document, function (node) { + const walkDocument = (document) => { + walk(document, (node) => { if (inFragment) { if (node === currentFragment._fragmentEnd) { inFragment = false @@ -57,7 +56,8 @@ export function scan () { for (const iframe of iframes) { try { walkDocument(iframe.contentDocument) - } catch (e) { + } + catch (e) { // Ignore } } @@ -68,11 +68,13 @@ export function scan () { for (const customTarget of customTargets) { try { walkDocument(customTarget) - } catch (e) { + } + catch (e) { // Ignore } } - } else { + } + else { if (Array.isArray(target.__VUE_ROOT_INSTANCES__)) { target.__VUE_ROOT_INSTANCES__.map(processInstance) } @@ -88,7 +90,7 @@ export function scan () { * @param {Function} fn */ -function walk (node, fn) { +function walk(node, fn) { if (node.childNodes) { for (let i = 0, l = node.childNodes.length; i < l; i++) { const child = node.childNodes[i] diff --git a/packages/app-backend-core/src/page-config.ts b/packages/app-backend-core/src/page-config.ts index 42b627a3e..d570ae97f 100644 --- a/packages/app-backend-core/src/page-config.ts +++ b/packages/app-backend-core/src/page-config.ts @@ -1,4 +1,4 @@ -import { target, SharedData } from '@vue-devtools/shared-utils' +import { SharedData, target } from '@vue-devtools/shared-utils' export interface PageConfig { openInEditorHost?: string @@ -8,11 +8,11 @@ export interface PageConfig { let config: PageConfig = {} -export function getPageConfig (): PageConfig { +export function getPageConfig(): PageConfig { return config } -export function initOnPageConfig () { +export function initOnPageConfig() { // User project devtools config if (Object.hasOwnProperty.call(target, 'VUE_DEVTOOLS_CONFIG')) { config = SharedData.pageConfig = target.VUE_DEVTOOLS_CONFIG diff --git a/packages/app-backend-core/src/perf.ts b/packages/app-backend-core/src/perf.ts index 9e487396e..4cfa2f03c 100644 --- a/packages/app-backend-core/src/perf.ts +++ b/packages/app-backend-core/src/perf.ts @@ -1,12 +1,20 @@ -import { BackendContext, DevtoolsBackend } from '@vue-devtools/app-backend-api' -import { App, ComponentInstance } from '@vue/devtools-api' -import { BridgeSubscriptions, raf, SharedData } from '@vue-devtools/shared-utils' +import type { BackendContext, DevtoolsBackend } from '@vue-devtools/app-backend-api' +import type { App, ComponentInstance } from '@vue/devtools-api' +import { BridgeSubscriptions, SharedData, raf } from '@vue-devtools/shared-utils' import { addTimelineEvent } from './timeline' import { getAppRecord } from './app' import { getComponentId, sendComponentTreeData } from './component' import { isSubscribed } from './util/subscriptions' -export async function performanceMarkStart ( +const markEndQueue = new Map() + +export async function performanceMarkStart( app: App, uid: number, instance: ComponentInstance, @@ -15,9 +23,13 @@ export async function performanceMarkStart ( ctx: BackendContext, ) { try { - if (!SharedData.performanceMonitoringEnabled) return + if (!SharedData.performanceMonitoringEnabled) { + return + } const appRecord = await getAppRecord(app, ctx) - if (!appRecord) return + if (!appRecord) { + return + } const componentName = await appRecord.backend.api.getComponentName(instance) const groupId = ctx.perfUniqueGroupId++ const groupKey = `${uid}-${type}` @@ -55,22 +67,15 @@ export async function performanceMarkStart ( ctx, ) } - } catch (e) { + } + catch (e) { if (SharedData.debugInfo) { console.error(e) } } } -const markEndQueue = new Map() - -export async function performanceMarkEnd ( +export async function performanceMarkEnd( app: App, uid: number, instance: ComponentInstance, @@ -79,9 +84,13 @@ export async function performanceMarkEnd ( ctx: BackendContext, ) { try { - if (!SharedData.performanceMonitoringEnabled) return + if (!SharedData.performanceMonitoringEnabled) { + return + } const appRecord = await getAppRecord(app, ctx) - if (!appRecord) return + if (!appRecord) { + return + } const componentName = await appRecord.backend.api.getComponentName(instance) const groupKey = `${uid}-${type}` const groupInfo = appRecord.perfGroupIds.get(groupKey) @@ -152,15 +161,16 @@ export async function performanceMarkEnd ( } } } - } catch (e) { + } + catch (e) { if (SharedData.debugInfo) { console.error(e) } } } -export function handleAddPerformanceTag (backend: DevtoolsBackend, ctx: BackendContext) { - backend.api.on.visitComponentTree(payload => { +export function handleAddPerformanceTag(backend: DevtoolsBackend, _ctx: BackendContext) { + backend.api.on.visitComponentTree((payload) => { if (payload.componentInstance.__VUE_DEVTOOLS_SLOW__) { const { duration, measures } = payload.componentInstance.__VUE_DEVTOOLS_SLOW__ diff --git a/packages/app-backend-core/src/plugin.ts b/packages/app-backend-core/src/plugin.ts index 20abbb81f..cf726df92 100644 --- a/packages/app-backend-core/src/plugin.ts +++ b/packages/app-backend-core/src/plugin.ts @@ -1,9 +1,10 @@ -import { PluginQueueItem } from '@vue/devtools-api' -import { Plugin, BackendContext, DevtoolsPluginApiInstance } from '@vue-devtools/app-backend-api' +import type { PluginQueueItem } from '@vue/devtools-api' +import type { BackendContext, Plugin } from '@vue-devtools/app-backend-api' +import { DevtoolsPluginApiInstance } from '@vue-devtools/app-backend-api' import { BridgeEvents, SharedData, target } from '@vue-devtools/shared-utils' import { getAppRecord, getAppRecordId } from './app' -export async function addPlugin (pluginQueueItem: PluginQueueItem, ctx: BackendContext) { +export async function addPlugin(pluginQueueItem: PluginQueueItem, ctx: BackendContext) { const { pluginDescriptor, setupFn } = pluginQueueItem const plugin: Plugin = { @@ -14,14 +15,18 @@ export async function addPlugin (pluginQueueItem: PluginQueueItem, ctx: BackendC ctx.currentPlugin = plugin try { const appRecord = await getAppRecord(plugin.descriptor.app, ctx) - if (!appRecord) return + if (!appRecord) { + return + } const api = new DevtoolsPluginApiInstance(plugin, appRecord, ctx) if (pluginQueueItem.proxy) { await pluginQueueItem.proxy.setRealTarget(api) - } else { + } + else { setupFn(api) } - } catch (e) { + } + catch (e) { plugin.error = e if (SharedData.debugInfo) { console.error(e) @@ -40,7 +45,7 @@ export async function addPlugin (pluginQueueItem: PluginQueueItem, ctx: BackendC }) } -export async function addQueuedPlugins (ctx: BackendContext) { +export async function addQueuedPlugins(ctx: BackendContext) { if (target.__VUE_DEVTOOLS_PLUGINS__ && Array.isArray(target.__VUE_DEVTOOLS_PLUGINS__)) { for (const queueItem of target.__VUE_DEVTOOLS_PLUGINS__) { await addPlugin(queueItem, ctx) @@ -49,7 +54,7 @@ export async function addQueuedPlugins (ctx: BackendContext) { } } -export async function addPreviouslyRegisteredPlugins (ctx: BackendContext) { +export async function addPreviouslyRegisteredPlugins(ctx: BackendContext) { if (target.__VUE_DEVTOOLS_REGISTERED_PLUGINS__ && Array.isArray(target.__VUE_DEVTOOLS_REGISTERED_PLUGINS__)) { for (const queueItem of target.__VUE_DEVTOOLS_REGISTERED_PLUGINS__) { await addPlugin(queueItem, ctx) @@ -57,13 +62,13 @@ export async function addPreviouslyRegisteredPlugins (ctx: BackendContext) { } } -export async function sendPluginList (ctx: BackendContext) { +export async function sendPluginList(ctx: BackendContext) { ctx.bridge.send(BridgeEvents.TO_FRONT_DEVTOOLS_PLUGIN_LIST, { plugins: await Promise.all(ctx.plugins.map(p => serializePlugin(p))), }) } -export async function serializePlugin (plugin: Plugin) { +export async function serializePlugin(plugin: Plugin) { return { id: plugin.descriptor.id, label: plugin.descriptor.label, diff --git a/packages/app-backend-core/src/timeline-builtins.ts b/packages/app-backend-core/src/timeline-builtins.ts index 3322d27ed..9b365c9c5 100644 --- a/packages/app-backend-core/src/timeline-builtins.ts +++ b/packages/app-backend-core/src/timeline-builtins.ts @@ -1,15 +1,15 @@ -import { TimelineLayerOptions } from '@vue/devtools-api' +import type { TimelineLayerOptions } from '@vue/devtools-api' export const builtinLayers: TimelineLayerOptions[] = [ { id: 'mouse', label: 'Mouse', color: 0xA451AF, - screenshotOverlayRender (event, { events }) { + screenshotOverlayRender(event, { events }) { const samePositionEvent = events.find(e => e !== event && e.renderMeta.textEl && e.data.x === event.data.x && e.data.y === event.data.y) if (samePositionEvent) { const text = document.createElement('div') - text.innerText = event.data.type + text.textContent = event.data.type samePositionEvent.renderMeta.textEl.appendChild(text) return false } @@ -24,7 +24,7 @@ export const builtinLayers: TimelineLayerOptions[] = [ div.style.backgroundColor = 'rgba(164, 81, 175, 0.5)' const text = document.createElement('div') - text.innerText = event.data.type + text.textContent = event.data.type text.style.color = '#541e5b' text.style.fontFamily = 'monospace' text.style.fontSize = '9px' @@ -52,10 +52,10 @@ export const builtinLayers: TimelineLayerOptions[] = [ color: 0x41B883, screenshotOverlayRender: (event, { events }) => { if (!event.meta.bounds || events.some(e => e !== event && e.layerId === event.layerId && e.renderMeta.drawn && (e.meta.componentId === event.meta.componentId || ( - e.meta.bounds.left === event.meta.bounds.left && - e.meta.bounds.top === event.meta.bounds.top && - e.meta.bounds.width === event.meta.bounds.width && - e.meta.bounds.height === event.meta.bounds.height + e.meta.bounds.left === event.meta.bounds.left + && e.meta.bounds.top === event.meta.bounds.top + && e.meta.bounds.width === event.meta.bounds.width + && e.meta.bounds.height === event.meta.bounds.height )))) { return false } @@ -83,7 +83,7 @@ export const builtinLayers: TimelineLayerOptions[] = [ text.style.padding = '1px' text.style.backgroundColor = 'rgba(255, 255, 255, 0.9)' text.style.borderRadius = '3px' - text.innerText = event.data.event + text.textContent = event.data.event div.appendChild(text) event.renderMeta.drawn = true @@ -94,7 +94,7 @@ export const builtinLayers: TimelineLayerOptions[] = [ { id: 'performance', label: 'Performance', - color: 0x41b86a, + color: 0x41B86A, groupsOnly: true, skipScreenshots: true, ignoreNoDurationGroups: true, diff --git a/packages/app-backend-core/src/timeline-marker.ts b/packages/app-backend-core/src/timeline-marker.ts index 104c1b91f..95546fd82 100644 --- a/packages/app-backend-core/src/timeline-marker.ts +++ b/packages/app-backend-core/src/timeline-marker.ts @@ -1,9 +1,10 @@ -import { BackendContext, TimelineMarker } from '@vue-devtools/app-backend-api' +import type { BackendContext, TimelineMarker } from '@vue-devtools/app-backend-api' import { BridgeEvents } from '@vue-devtools/shared-utils' -import { isPerformanceSupported, TimelineMarkerOptions } from '@vue/devtools-api' +import type { TimelineMarkerOptions } from '@vue/devtools-api' +import { isPerformanceSupported } from '@vue/devtools-api' import { dateThreshold, perfTimeDiff } from './timeline' -export async function addTimelineMarker (options: TimelineMarkerOptions, ctx: BackendContext) { +export async function addTimelineMarker(options: TimelineMarkerOptions, ctx: BackendContext) { if (!ctx.currentAppRecord) { options.all = true } @@ -18,8 +19,10 @@ export async function addTimelineMarker (options: TimelineMarkerOptions, ctx: Ba }) } -export async function sendTimelineMarkers (ctx: BackendContext) { - if (!ctx.currentAppRecord) return +export async function sendTimelineMarkers(ctx: BackendContext) { + if (!ctx.currentAppRecord) { + return + } const markers = ctx.timelineMarkers.filter(marker => marker.all || marker.appRecord === ctx.currentAppRecord) const result = [] for (const marker of markers) { @@ -31,7 +34,7 @@ export async function sendTimelineMarkers (ctx: BackendContext) { }) } -async function serializeMarker (marker: TimelineMarker) { +async function serializeMarker(marker: TimelineMarker) { let time = marker.time if (isPerformanceSupported() && time < dateThreshold) { time += perfTimeDiff diff --git a/packages/app-backend-core/src/timeline-screenshot.ts b/packages/app-backend-core/src/timeline-screenshot.ts index 3a99763d5..1b11a5f71 100644 --- a/packages/app-backend-core/src/timeline-screenshot.ts +++ b/packages/app-backend-core/src/timeline-screenshot.ts @@ -1,5 +1,5 @@ -import { BackendContext } from '@vue-devtools/app-backend-api' -import { ID, ScreenshotOverlayRenderContext } from '@vue/devtools-api' +import type { BackendContext } from '@vue-devtools/app-backend-api' +import type { ID, ScreenshotOverlayRenderContext } from '@vue/devtools-api' import { SharedData } from '@vue-devtools/shared-utils' import { JobQueue } from './util/queue' import { builtinLayers } from './timeline-builtins' @@ -17,7 +17,7 @@ interface Screenshot { events: ID[] } -export async function showScreenshot (screenshot: Screenshot, ctx: BackendContext) { +export async function showScreenshot(screenshot: Screenshot, ctx: BackendContext) { await jobQueue.queue('showScreenshot', async () => { if (screenshot) { if (!container) { @@ -53,11 +53,13 @@ export async function showScreenshot (screenshot: Screenshot, ctx: BackendContex if (result !== false) { if (typeof result === 'string') { container.innerHTML += result - } else { + } + else { container.appendChild(result) } } - } catch (e) { + } + catch (e) { if (SharedData.debugInfo) { console.error(e) } @@ -66,13 +68,14 @@ export async function showScreenshot (screenshot: Screenshot, ctx: BackendContex } showElement() - } else { + } + else { hideElement() } }) } -function createElements () { +function createElements() { overlay = document.createElement('div') overlay.style.position = 'fixed' overlay.style.zIndex = '9999999999999' @@ -102,14 +105,14 @@ function createElements () { document.head.appendChild(style) } -function showElement () { +function showElement() { if (!overlay.parentNode) { document.body.appendChild(overlay) document.body.classList.add('__vuedevtools_no-scroll') } } -function hideElement () { +function hideElement() { if (overlay && overlay.parentNode) { overlay.parentNode.removeChild(overlay) @@ -119,7 +122,7 @@ function hideElement () { } } -function clearContent () { +function clearContent() { while (container.firstChild) { container.removeChild(container.lastChild) } diff --git a/packages/app-backend-core/src/timeline.ts b/packages/app-backend-core/src/timeline.ts index b7cda33be..3d8fcbba8 100644 --- a/packages/app-backend-core/src/timeline.ts +++ b/packages/app-backend-core/src/timeline.ts @@ -1,15 +1,16 @@ -import { BackendContext, AppRecord } from '@vue-devtools/app-backend-api' -import { BridgeEvents, HookEvents, stringify, SharedData, isBrowser } from '@vue-devtools/shared-utils' -import { App, ID, TimelineEventOptions, WithId, now, isPerformanceSupported } from '@vue/devtools-api' +import type { AppRecord, BackendContext } from '@vue-devtools/app-backend-api' +import { BridgeEvents, HookEvents, SharedData, isBrowser, stringify } from '@vue-devtools/shared-utils' +import type { App, ID, TimelineEventOptions, WithId } from '@vue/devtools-api' +import { isPerformanceSupported, now } from '@vue/devtools-api' import { hook } from './global-hook' import { getAppRecord, getAppRecordId } from './app' import { builtinLayers } from './timeline-builtins' -export function setupTimeline (ctx: BackendContext) { +export function setupTimeline(ctx: BackendContext) { setupBuiltinLayers(ctx) } -export function addBuiltinLayers (appRecord: AppRecord, ctx: BackendContext) { +export function addBuiltinLayers(appRecord: AppRecord, ctx: BackendContext) { for (const layerDef of builtinLayers) { ctx.timelineLayers.push({ ...layerDef, @@ -20,10 +21,9 @@ export function addBuiltinLayers (appRecord: AppRecord, ctx: BackendContext) { } } -function setupBuiltinLayers (ctx: BackendContext) { +function setupBuiltinLayers(ctx: BackendContext) { if (isBrowser) { - ['mousedown', 'mouseup', 'click', 'dblclick'].forEach(eventType => { - // @ts-ignore + (['mousedown', 'mouseup', 'click', 'dblclick'] as const).forEach((eventType) => { window.addEventListener(eventType, async (event: MouseEvent) => { await addTimelineEvent({ layerId: 'mouse', @@ -43,8 +43,7 @@ function setupBuiltinLayers (ctx: BackendContext) { }) }) - ;['keyup', 'keydown', 'keypress'].forEach(eventType => { - // @ts-ignore + ;(['keyup', 'keydown', 'keypress'] as const).forEach((eventType) => { window.addEventListener(eventType, async (event: KeyboardEvent) => { await addTimelineEvent({ layerId: 'keyboard', @@ -70,10 +69,14 @@ function setupBuiltinLayers (ctx: BackendContext) { hook.on(HookEvents.COMPONENT_EMIT, async (app, instance, event, params) => { try { - if (!SharedData.componentEventsEnabled) return + if (!SharedData.componentEventsEnabled) { + return + } const appRecord = await getAppRecord(app, ctx) - if (!appRecord) return + if (!appRecord) { + return + } const componentId = `${appRecord.id}:${instance.uid}` const componentDisplay = (await appRecord.backend.api.getComponentName(instance)) || 'Unknown Component' @@ -99,7 +102,8 @@ function setupBuiltinLayers (ctx: BackendContext) { }, }, }, app, ctx) - } catch (e) { + } + catch (e) { if (SharedData.debugInfo) { console.error(e) } @@ -107,7 +111,7 @@ function setupBuiltinLayers (ctx: BackendContext) { }) } -export async function sendTimelineLayers (ctx: BackendContext) { +export async function sendTimelineLayers(ctx: BackendContext) { const layers = [] for (const layer of ctx.timelineLayers) { try { @@ -121,7 +125,8 @@ export async function sendTimelineLayers (ctx: BackendContext) { skipScreenshots: layer.skipScreenshots, ignoreNoDurationGroups: layer.ignoreNoDurationGroups, }) - } catch (e) { + } + catch (e) { if (SharedData.debugInfo) { console.error(e) } @@ -132,7 +137,7 @@ export async function sendTimelineLayers (ctx: BackendContext) { }) } -export async function addTimelineEvent (options: TimelineEventOptions, app: App, ctx: BackendContext) { +export async function addTimelineEvent(options: TimelineEventOptions, app: App, ctx: BackendContext) { const appId = app ? getAppRecordId(app) : null const isAllApps = options.all || !app || appId == null @@ -154,7 +159,8 @@ export async function addTimelineEvent (options: TimelineEventOptions, app: App, const layer = ctx.timelineLayers.find(l => (isAllApps || l.appRecord?.options.app === app) && l.id === options.layerId) if (layer) { layer.events.push(eventData) - } else if (SharedData.debugInfo) { + } + else if (SharedData.debugInfo) { console.warn(`Timeline layer ${options.layerId} not found`) } } @@ -163,7 +169,7 @@ const initialTime = Date.now() export const dateThreshold = initialTime - 1_000_000 export const perfTimeDiff = initialTime - now() -function mapTimelineEvent (eventData: TimelineEventOptions & WithId) { +function mapTimelineEvent(eventData: TimelineEventOptions & WithId) { let time = eventData.event.time if (isPerformanceSupported() && time < dateThreshold) { time += perfTimeDiff @@ -178,7 +184,7 @@ function mapTimelineEvent (eventData: TimelineEventOptions & WithId) { } } -export async function clearTimeline (ctx: BackendContext) { +export async function clearTimeline(ctx: BackendContext) { ctx.timelineEventMap.clear() for (const layer of ctx.timelineLayers) { layer.events = [] @@ -188,13 +194,14 @@ export async function clearTimeline (ctx: BackendContext) { } } -export async function sendTimelineEventData (id: ID, ctx: BackendContext) { +export async function sendTimelineEventData(id: ID, ctx: BackendContext) { let data = null const eventData = ctx.timelineEventMap.get(id) if (eventData) { data = await ctx.currentAppRecord.backend.api.inspectTimelineEvent(eventData, ctx.currentAppRecord.options.app) data = stringify(data) - } else if (SharedData.debugInfo) { + } + else if (SharedData.debugInfo) { console.warn(`Event ${id} not found`, ctx.timelineEventMap.keys()) } ctx.bridge.send(BridgeEvents.TO_FRONT_TIMELINE_EVENT_DATA, { @@ -203,22 +210,28 @@ export async function sendTimelineEventData (id: ID, ctx: BackendContext) { }) } -export function removeLayersForApp (app: App, ctx: BackendContext) { +export function removeLayersForApp(app: App, ctx: BackendContext) { const layers = ctx.timelineLayers.filter(l => l.appRecord?.options.app === app) for (const layer of layers) { const index = ctx.timelineLayers.indexOf(layer) - if (index !== -1) ctx.timelineLayers.splice(index, 1) + if (index !== -1) { + ctx.timelineLayers.splice(index, 1) + } for (const e of layer.events) { ctx.timelineEventMap.delete(e.id) } } } -export function sendTimelineLayerEvents (appId: string, layerId: string, ctx: BackendContext) { +export function sendTimelineLayerEvents(appId: string, layerId: string, ctx: BackendContext) { const app = ctx.appRecords.find(ar => ar.id === appId)?.options.app - if (!app) return + if (!app) { + return + } const layer = ctx.timelineLayers.find(l => l.appRecord?.options.app === app && l.id === layerId) - if (!layer) return + if (!layer) { + return + } ctx.bridge.send(BridgeEvents.TO_FRONT_TIMELINE_LAYER_LOAD_EVENTS, { appId, layerId, diff --git a/packages/app-backend-core/src/toast.ts b/packages/app-backend-core/src/toast.ts index ff91ab404..ff2811968 100644 --- a/packages/app-backend-core/src/toast.ts +++ b/packages/app-backend-core/src/toast.ts @@ -1,3 +1,3 @@ -export function installToast () { +export function installToast() { // @TODO } diff --git a/packages/app-backend-core/src/util/queue.ts b/packages/app-backend-core/src/util/queue.ts index 087a03b08..99932bbb4 100644 --- a/packages/app-backend-core/src/util/queue.ts +++ b/packages/app-backend-core/src/util/queue.ts @@ -7,13 +7,13 @@ export class JobQueue { jobs: Job[] = [] currentJob: Job - queue (id: string, fn: Job['fn']) { + queue(id: string, fn: Job['fn']) { const job: Job = { id, fn, } - return new Promise(resolve => { + return new Promise((resolve) => { const onDone = () => { this.currentJob = null const nextJob = this.jobs.shift() @@ -25,7 +25,7 @@ export class JobQueue { const run = () => { this.currentJob = job - return job.fn().then(onDone).catch(e => { + return job.fn().then(onDone).catch((e) => { console.error(`Job ${job.id} failed:`) console.error(e) }) @@ -36,7 +36,8 @@ export class JobQueue { id: job.id, fn: () => run(), }) - } else { + } + else { run() } }) diff --git a/packages/app-backend-core/src/util/subscriptions.ts b/packages/app-backend-core/src/util/subscriptions.ts index c0a86daa7..d57bdbce6 100644 --- a/packages/app-backend-core/src/util/subscriptions.ts +++ b/packages/app-backend-core/src/util/subscriptions.ts @@ -1,6 +1,6 @@ const activeSubs: Map> = new Map() -function getSubs (type: string) { +function getSubs(type: string) { let subs = activeSubs.get(type) if (!subs) { subs = new Map() @@ -9,16 +9,16 @@ function getSubs (type: string) { return subs } -export function subscribe (type: string, key: string) { +export function subscribe(type: string, key: string) { getSubs(type).set(key, true) } -export function unsubscribe (type: string, key: string) { +export function unsubscribe(type: string, key: string) { const subs = getSubs(type) subs.delete(key) } -export function isSubscribed ( +export function isSubscribed( type: string, key: string, ) { diff --git a/packages/app-backend-core/tsconfig.json b/packages/app-backend-core/tsconfig.json index f565bc660..becb763b2 100644 --- a/packages/app-backend-core/tsconfig.json +++ b/packages/app-backend-core/tsconfig.json @@ -3,25 +3,25 @@ "target": "ES2019", "module": "commonjs", "moduleResolution": "node", - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, "resolveJsonModule": true, - "skipLibCheck": true, "types": [ "node", "webpack-env" ], - "sourceMap": true, - "preserveWatchOutput": true, + "strictBindCallApply": true, + "strictFunctionTypes": true, + "alwaysStrict": true, // Strict "noImplicitAny": false, "noImplicitThis": true, - "alwaysStrict": true, - "strictBindCallApply": true, - "strictFunctionTypes": true, + "sourceMap": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "skipLibCheck": true, + "preserveWatchOutput": true }, "include": [ - "src/**/*", + "src/**/*" ], "exclude": [ "node_modules" diff --git a/packages/app-backend-vue1/package.json b/packages/app-backend-vue1/package.json index e74abf949..76cf42fe9 100644 --- a/packages/app-backend-vue1/package.json +++ b/packages/app-backend-vue1/package.json @@ -14,8 +14,8 @@ "@vue-devtools/shared-utils": "^0.0.0" }, "devDependencies": { - "@types/node": "^13.9.1", + "@types/node": "^20.11.16", "@types/webpack-env": "^1.15.1", - "typescript": "^4.5.2" + "typescript": "^5.3.3" } } diff --git a/packages/app-backend-vue1/src/index.ts b/packages/app-backend-vue1/src/index.ts index a4c7264ac..8798cc072 100644 --- a/packages/app-backend-vue1/src/index.ts +++ b/packages/app-backend-vue1/src/index.ts @@ -3,7 +3,7 @@ import { defineBackend } from '@vue-devtools/app-backend-api' export const backend = defineBackend({ frameworkVersion: 1, features: [], - setup (api) { + setup(_api) { // @TODO }, }) diff --git a/packages/app-backend-vue1/tsconfig.json b/packages/app-backend-vue1/tsconfig.json index f565bc660..becb763b2 100644 --- a/packages/app-backend-vue1/tsconfig.json +++ b/packages/app-backend-vue1/tsconfig.json @@ -3,25 +3,25 @@ "target": "ES2019", "module": "commonjs", "moduleResolution": "node", - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, "resolveJsonModule": true, - "skipLibCheck": true, "types": [ "node", "webpack-env" ], - "sourceMap": true, - "preserveWatchOutput": true, + "strictBindCallApply": true, + "strictFunctionTypes": true, + "alwaysStrict": true, // Strict "noImplicitAny": false, "noImplicitThis": true, - "alwaysStrict": true, - "strictBindCallApply": true, - "strictFunctionTypes": true, + "sourceMap": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "skipLibCheck": true, + "preserveWatchOutput": true }, "include": [ - "src/**/*", + "src/**/*" ], "exclude": [ "node_modules" diff --git a/packages/app-backend-vue2/package.json b/packages/app-backend-vue2/package.json index 654595d71..1a95fec7d 100644 --- a/packages/app-backend-vue2/package.json +++ b/packages/app-backend-vue2/package.json @@ -17,11 +17,11 @@ "lodash": "^4.17.21" }, "devDependencies": { - "vue": "^2.7.10", - "vue-loader": "^15.7.1", - "@types/node": "^13.9.1", + "@types/node": "^20.11.16", "@types/webpack-env": "^1.15.1", "core-js": "^3.20.2", - "typescript": "^4.5.2" + "typescript": "^5.3.3", + "vue": "^2.7.10", + "vue-loader": "^15.7.1" } } diff --git a/packages/app-backend-vue2/src/components/data.ts b/packages/app-backend-vue2/src/components/data.ts index dcb0c5d0a..227e55dd9 100644 --- a/packages/app-backend-vue2/src/components/data.ts +++ b/packages/app-backend-vue2/src/components/data.ts @@ -1,16 +1,19 @@ -import { camelize, getComponentName, getCustomRefDetails, StateEditor, SharedData } from '@vue-devtools/shared-utils' -import { ComponentState, CustomState, HookPayloads, Hooks, InspectedComponentData } from '@vue/devtools-api' -import { functionalVnodeMap, instanceMap } from './tree' +import type { StateEditor } from '@vue-devtools/shared-utils' +import { SharedData, camelize, getComponentName, getCustomRefDetails } from '@vue-devtools/shared-utils' +import type { ComponentState, CustomState, HookPayloads, Hooks, InspectedComponentData } from '@vue/devtools-api' +import { getFunctionalVnodeMap, getInstanceMap } from './tree' import 'core-js/modules/es.object.entries' /** * Get the detailed information of an inspected instance. */ -export function getInstanceDetails (instance): InspectedComponentData { +export function getInstanceDetails(instance): InspectedComponentData { if (instance.__VUE_DEVTOOLS_FUNCTIONAL_LEGACY__) { const vnode = findInstanceOrVnode(instance.__VUE_DEVTOOLS_UID__) - if (!vnode) return null + if (!vnode) { + return null + } const fakeInstance = { $options: vnode.fnOptions, @@ -42,15 +45,14 @@ export function getInstanceDetails (instance): InspectedComponentData { file: null, } - let i - if ((i = instance.$vnode) && (i = i.componentOptions) && (i = i.Ctor) && (i = i.options)) { - data.file = i.__file || null + if (instance.$vnode?.componentOptions?.Ctor?.options) { + data.file = instance.$vnode.componentOptions.Ctor.options.__file || null } return data } -function getInstanceState (instance): ComponentState[] { +function getInstanceState(instance): ComponentState[] { return processProps(instance).concat( processState(instance), processSetupState(instance), @@ -65,11 +67,11 @@ function getInstanceState (instance): ComponentState[] { ) } -function getFunctionalInstanceState (instance): ComponentState[] { +function getFunctionalInstanceState(instance): ComponentState[] { return processProps(instance) } -export function getCustomInstanceDetails (instance) { +export function getCustomInstanceDetails(instance) { const state = getInstanceState(instance) return { _custom: { @@ -85,7 +87,7 @@ export function getCustomInstanceDetails (instance) { } } -export function reduceStateList (list) { +export function reduceStateList(list) { if (!list.length) { return undefined } @@ -100,9 +102,11 @@ export function reduceStateList (list) { /** * Get the appropriate display name for an instance. */ -export function getInstanceName (instance): string { +export function getInstanceName(instance): string { const name = getComponentName(instance.$options || instance.fnOptions || {}) - if (name) return name + if (name) { + return name + } return instance.$root === instance ? 'Root' : 'Anonymous Component' @@ -113,7 +117,7 @@ export function getInstanceName (instance): string { * Make sure return a plain object because window.postMessage() * will throw an Error if the passed object contains Functions. */ -function processProps (instance): ComponentState[] { +function processProps(instance): ComponentState[] { const props = instance.$options.props const propsData = [] for (let key in props) { @@ -137,7 +141,7 @@ function processProps (instance): ComponentState[] { return propsData } -function processAttrs (instance): ComponentState[] { +function processAttrs(instance): ComponentState[] { return Object.entries(instance.$attrs || {}).map(([key, value]) => { return { type: '$attrs', @@ -152,7 +156,7 @@ const fnTypeRE = /^(?:function|class) (\w+)/ /** * Convert prop type constructor to string. */ -function getPropType (type) { +function getPropType(type) { if (Array.isArray(type)) { return type.map(t => getPropType(t)).join(' or ') } @@ -170,15 +174,15 @@ function getPropType (type) { * with a JSON dance. This removes functions which can cause * errors during structured clone used by window.postMessage. */ -function processState (instance): ComponentState[] { +function processState(instance): ComponentState[] { const props = instance.$options.props - const getters = - instance.$options.vuex && - instance.$options.vuex.getters + const getters + = instance.$options.vuex + && instance.$options.vuex.getters return Object.keys(instance._data) .filter(key => ( - !(props && key in props) && - !(getters && key in getters) + !(props && key in props) + && !(getters && key in getters) )) .map(key => ({ key, @@ -188,7 +192,7 @@ function processState (instance): ComponentState[] { })) } -function processSetupState (instance) { +function processSetupState(instance) { const state = instance._setupProxy || instance const raw = instance._setupState if (!raw) { @@ -197,7 +201,7 @@ function processSetupState (instance) { return Object.keys(raw) .filter(key => !key.startsWith('__')) - .map(key => { + .map((key) => { const value = returnError(() => toRaw(state[key])) const rawData = raw[key] @@ -219,7 +223,8 @@ function processSetupState (instance) { editable: isState && !info.readonly, type: isOther ? 'setup (other)' : 'setup', } - } else { + } + else { result = { type: 'setup', } @@ -233,38 +238,39 @@ function processSetupState (instance) { }) } -function returnError (cb: () => any) { +function returnError(cb: () => any) { try { return cb() - } catch (e) { + } + catch (e) { return e } } -function isRef (raw: any): boolean { +function isRef(raw: any): boolean { return !!raw.__v_isRef } -function isComputed (raw: any): boolean { +function isComputed(raw: any): boolean { return isRef(raw) && !!raw.effect } -function isReactive (raw: any): boolean { +function isReactive(raw: any): boolean { return !!raw.__ob__ } -function isReadOnly (raw: any): boolean { +function isReadOnly(raw: any): boolean { return !!raw.__v_isReadonly } -function toRaw (value: any) { +function toRaw(value: any) { if (value?.__v_raw) { return value.__v_raw } return value } -function getSetupStateInfo (raw: any) { +function getSetupStateInfo(raw: any) { return { ref: isRef(raw), computed: isComputed(raw), @@ -273,7 +279,7 @@ function getSetupStateInfo (raw: any) { } } -export function getCustomObjectDetails (object: any, proto: string): CustomState | undefined { +export function getCustomObjectDetails(object: any, _proto: string): CustomState | undefined { const info = getSetupStateInfo(object) const isState = info.ref || info.computed || info.reactive @@ -295,7 +301,7 @@ export function getCustomObjectDetails (object: any, proto: string): CustomState /** * Process refs */ -function processRefs (instance): ComponentState[] { +function processRefs(instance): ComponentState[] { return Object.keys(instance.$refs) .filter(key => instance.$refs[key]) .map(key => getCustomRefDetails(instance, key, instance.$refs[key])) @@ -304,7 +310,7 @@ function processRefs (instance): ComponentState[] { /** * Process the computed properties of an instance. */ -function processComputed (instance): ComponentState[] { +function processComputed(instance): ComponentState[] { const computed = [] const defs = instance.$options.computed || {} // use for...in here because if 'computed' is not defined @@ -325,7 +331,8 @@ function processComputed (instance): ComponentState[] { key, value: instance[key], } - } catch (e) { + } + catch (e) { computedProp = { type, key, @@ -342,18 +349,19 @@ function processComputed (instance): ComponentState[] { /** * Process Vuex getters. */ -function processInjected (instance): ComponentState[] { +function processInjected(instance): ComponentState[] { const injected = instance.$options.inject if (injected) { - return Object.keys(injected).map(key => { + return Object.keys(injected).map((key) => { return { key, type: 'injected', value: instance[key], } }) - } else { + } + else { return [] } } @@ -361,16 +369,24 @@ function processInjected (instance): ComponentState[] { /** * Process possible vue-router $route context */ -function processRouteContext (instance): ComponentState[] { +function processRouteContext(instance): ComponentState[] { try { const route = instance.$route if (route) { const { path, query, params } = route const value: any = { path, query, params } - if (route.fullPath) value.fullPath = route.fullPath - if (route.hash) value.hash = route.hash - if (route.name) value.name = route.name - if (route.meta) value.meta = route.meta + if (route.fullPath) { + value.fullPath = route.fullPath + } + if (route.hash) { + value.hash = route.hash + } + if (route.name) { + value.name = route.name + } + if (route.meta) { + value.meta = route.meta + } return [{ key: '$route', type: 'route', @@ -383,7 +399,8 @@ function processRouteContext (instance): ComponentState[] { }, }] } - } catch (e) { + } + catch (e) { // Invalid $router } return [] @@ -392,19 +409,20 @@ function processRouteContext (instance): ComponentState[] { /** * Process Vuex getters. */ -function processVuexGetters (instance): ComponentState[] { - const getters = - instance.$options.vuex && - instance.$options.vuex.getters +function processVuexGetters(instance): ComponentState[] { + const getters + = instance.$options.vuex + && instance.$options.vuex.getters if (getters) { - return Object.keys(getters).map(key => { + return Object.keys(getters).map((key) => { return { type: 'vuex getters', key, value: instance[key], } }) - } else { + } + else { return [] } } @@ -412,17 +430,18 @@ function processVuexGetters (instance): ComponentState[] { /** * Process Firebase bindings. */ -function processFirebaseBindings (instance): ComponentState[] { +function processFirebaseBindings(instance): ComponentState[] { const refs = instance.$firebaseRefs if (refs) { - return Object.keys(refs).map(key => { + return Object.keys(refs).map((key) => { return { type: 'firebase bindings', key, value: instance[key], } }) - } else { + } + else { return [] } } @@ -430,31 +449,32 @@ function processFirebaseBindings (instance): ComponentState[] { /** * Process vue-rx observable bindings. */ -function processObservables (instance): ComponentState[] { +function processObservables(instance): ComponentState[] { const obs = instance.$observables if (obs) { - return Object.keys(obs).map(key => { + return Object.keys(obs).map((key) => { return { type: 'observables', key, value: instance[key], } }) - } else { + } + else { return [] } } -export function findInstanceOrVnode (id) { +export function findInstanceOrVnode(id) { if (/:functional:/.test(id)) { const [refId] = id.split(':functional:') - const map = functionalVnodeMap.get(refId) + const map = getFunctionalVnodeMap()?.get(refId) return map && map[id] } - return instanceMap.get(id) + return getInstanceMap()?.get(id) } -export function editState ( +export function editState( { componentInstance, path, @@ -463,7 +483,9 @@ export function editState ( }: HookPayloads[Hooks.EDIT_COMPONENT_STATE], stateEditor: StateEditor, ) { - if (!['data', 'props', 'computed', 'setup'].includes(type)) return + if (!['data', 'props', 'computed', 'setup'].includes(type)) { + return + } let target: any const targetPath: string[] = path.slice() @@ -471,9 +493,10 @@ export function editState ( if (stateEditor.has(componentInstance._props, path, !!state.newKey)) { // props target = componentInstance._props - } else if ( - componentInstance._setupState && - Object.keys(componentInstance._setupState).includes(path[0]) + } + else if ( + componentInstance._setupState + && Object.keys(componentInstance._setupState).includes(path[0]) ) { // setup target = componentInstance._setupProxy @@ -481,9 +504,12 @@ export function editState ( const currentValue = stateEditor.get(target, path) if (currentValue != null) { const info = getSetupStateInfo(currentValue) - if (info.readonly) return + if (info.readonly) { + return + } } - } else { + } + else { target = componentInstance._data } diff --git a/packages/app-backend-vue2/src/components/el.ts b/packages/app-backend-vue2/src/components/el.ts index b349d8ebb..bfd084ae6 100644 --- a/packages/app-backend-vue2/src/components/el.ts +++ b/packages/app-backend-vue2/src/components/el.ts @@ -1,18 +1,18 @@ import { inDoc, isBrowser, target } from '@vue-devtools/shared-utils' -function createRect () { +function createRect() { const rect = { top: 0, bottom: 0, left: 0, right: 0, - get width () { return rect.right - rect.left }, - get height () { return rect.bottom - rect.top }, + get width() { return rect.right - rect.left }, + get height() { return rect.bottom - rect.top }, } return rect } -function mergeRects (a, b) { +function mergeRects(a, b) { if (!a.top || b.top < a.top) { a.top = b.top } @@ -31,7 +31,7 @@ function mergeRects (a, b) { /** * Get the client rect for an instance. */ -export function getInstanceOrVnodeRect (instance) { +export function getInstanceOrVnodeRect(instance) { const el = instance.$el || instance.elm if (!isBrowser) { @@ -45,7 +45,8 @@ export function getInstanceOrVnodeRect (instance) { if (instance._isFragment) { return addIframePosition(getLegacyFragmentRect(instance), getElWindow(instance.$root.$el)) - } else if (el.nodeType === 1) { + } + else if (el.nodeType === 1) { return addIframePosition(el.getBoundingClientRect(), getElWindow(el)) } } @@ -54,13 +55,14 @@ export function getInstanceOrVnodeRect (instance) { * Highlight a fragment instance. * Loop over its node range and determine its bounding box. */ -function getLegacyFragmentRect ({ _fragmentStart, _fragmentEnd }) { +function getLegacyFragmentRect({ _fragmentStart, _fragmentEnd }) { const rect = createRect() - util().mapNodeRange(_fragmentStart, _fragmentEnd, function (node) { + util().mapNodeRange(_fragmentStart, _fragmentEnd, (node) => { let childRect if (node.nodeType === 1 || node.getBoundingClientRect) { childRect = node.getBoundingClientRect() - } else if (node.nodeType === 3 && node.data.trim()) { + } + else if (node.nodeType === 3 && node.data.trim()) { childRect = getTextRect(node) } if (childRect) { @@ -74,9 +76,13 @@ let range: Range /** * Get the bounding rect for a text node using a Range. */ -function getTextRect (node: Text) { - if (!isBrowser) return - if (!range) range = document.createRange() +function getTextRect(node: Text) { + if (!isBrowser) { + return + } + if (!range) { + range = document.createRange() + } range.selectNode(node) @@ -86,22 +92,22 @@ function getTextRect (node: Text) { /** * Get Vue's util */ -function util () { +function util() { return target.__VUE_DEVTOOLS_GLOBAL_HOOK__.Vue.util } -export function findRelatedComponent (el) { +export function findRelatedComponent(el) { while (!el.__vue__ && el.parentElement) { el = el.parentElement } return el.__vue__ } -function getElWindow (el: HTMLElement) { +function getElWindow(el: HTMLElement) { return el.ownerDocument.defaultView } -function addIframePosition (bounds, win: any) { +function addIframePosition(bounds, win: any) { if (win.__VUE_DEVTOOLS_IFRAME__) { const rect = mergeRects(createRect(), bounds) const iframeBounds = win.__VUE_DEVTOOLS_IFRAME__.getBoundingClientRect() @@ -117,11 +123,11 @@ function addIframePosition (bounds, win: any) { return bounds } -export function getRootElementsFromComponentInstance (instance) { +export function getRootElementsFromComponentInstance(instance) { if (instance._isFragment) { const list = [] const { _fragmentStart, _fragmentEnd } = instance - util().mapNodeRange(_fragmentStart, _fragmentEnd, node => { + util().mapNodeRange(_fragmentStart, _fragmentEnd, (node) => { list.push(node) }) return list diff --git a/packages/app-backend-vue2/src/components/perf.ts b/packages/app-backend-vue2/src/components/perf.ts index 73a6daee2..517cc1944 100644 --- a/packages/app-backend-vue2/src/components/perf.ts +++ b/packages/app-backend-vue2/src/components/perf.ts @@ -1,6 +1,6 @@ -import { DevtoolsApi } from '@vue-devtools/app-backend-api' +import type { DevtoolsApi } from '@vue-devtools/app-backend-api' import { HookEvents, SharedData } from '@vue-devtools/shared-utils' -import { instanceMap } from './tree' +import { getInstanceMap } from './tree' const COMPONENT_HOOKS = { beforeCreate: { start: 'create' }, @@ -13,20 +13,22 @@ const COMPONENT_HOOKS = { destroyed: { end: 'destroy' }, } -export function initPerf (api: DevtoolsApi, app, Vue) { +export function initPerf(api: DevtoolsApi, app, Vue) { // Global mixin Vue.mixin({ - beforeCreate () { + beforeCreate() { applyPerfHooks(api, this, app) }, }) // Apply to existing components - instanceMap?.forEach(vm => applyPerfHooks(api, vm, app)) + getInstanceMap()?.forEach(vm => applyPerfHooks(api, vm, app)) } -export function applyPerfHooks (api: DevtoolsApi, vm, app) { - if (vm.$options.$_devtoolsPerfHooks) return +export function applyPerfHooks(api: DevtoolsApi, vm, app) { + if (vm.$options.$_devtoolsPerfHooks) { + return + } vm.$options.$_devtoolsPerfHooks = true for (const hook in COMPONENT_HOOKS) { @@ -46,9 +48,11 @@ export function applyPerfHooks (api: DevtoolsApi, vm, app) { const currentValue = vm.$options[hook] if (Array.isArray(currentValue)) { vm.$options[hook] = [handler, ...currentValue] - } else if (typeof currentValue === 'function') { + } + else if (typeof currentValue === 'function') { vm.$options[hook] = [handler, currentValue] - } else { + } + else { vm.$options[hook] = [handler] } } diff --git a/packages/app-backend-vue2/src/components/tree.ts b/packages/app-backend-vue2/src/components/tree.ts index fa7d23d90..e73efa30a 100644 --- a/packages/app-backend-vue2/src/components/tree.ts +++ b/packages/app-backend-vue2/src/components/tree.ts @@ -1,13 +1,21 @@ -import { AppRecord, BackendContext, DevtoolsApi } from '@vue-devtools/app-backend-api' +import type { AppRecord, BackendContext, DevtoolsApi } from '@vue-devtools/app-backend-api' import { classify, kebabize } from '@vue-devtools/shared-utils' -import { ComponentTreeNode, ComponentInstance } from '@vue/devtools-api' +import type { ComponentInstance, ComponentTreeNode } from '@vue/devtools-api' import { getRootElementsFromComponentInstance } from './el' import { applyPerfHooks } from './perf.js' import { applyTrackingUpdateHook } from './update-tracking.js' import { getInstanceName, getRenderKey, getUniqueId, isBeingDestroyed } from './util' -export let instanceMap: Map -export let functionalVnodeMap: Map +let instanceMap: Map = new Map() +let functionalVnodeMap: Map = new Map() + +export function getInstanceMap() { + return instanceMap +} + +export function getFunctionalVnodeMap() { + return functionalVnodeMap +} let appRecord: AppRecord let api: DevtoolsApi @@ -22,7 +30,7 @@ const functionalIds = new Map() // Some instances may be both on a component and on a child abstract/functional component const captureIds = new Map() -export async function walkTree (instance, pFilter: string, pRecursively: boolean, api: DevtoolsApi, ctx: BackendContext): Promise { +export async function walkTree(instance, pFilter: string, pRecursively: boolean, api: DevtoolsApi, ctx: BackendContext): Promise { initCtx(api, ctx) filter = pFilter recursively = pRecursively @@ -32,17 +40,20 @@ export async function walkTree (instance, pFilter: string, pRecursively: boolean return result } -export function getComponentParents (instance, api: DevtoolsApi, ctx: BackendContext) { +export function getComponentParents(instance, api: DevtoolsApi, ctx: BackendContext) { initCtx(api, ctx) const captureIds = new Map() - const captureId = vm => { + const captureId = (vm) => { const id = vm.__VUE_DEVTOOLS_UID__ = getUniqueId(vm) - if (captureIds.has(id)) return + if (captureIds.has(id)) { + return + } captureIds.set(id, undefined) if (vm.__VUE_DEVTOOLS_FUNCTIONAL_LEGACY__) { markFunctional(id, vm.vnode) - } else { + } + else { mark(vm) } } @@ -50,6 +61,7 @@ export function getComponentParents (instance, api: DevtoolsApi, ctx: BackendCon const parents = [] captureId(instance) let parent = instance + // eslint-disable-next-line no-cond-assign while ((parent = parent.$parent)) { captureId(parent) parents.push(parent) @@ -57,7 +69,7 @@ export function getComponentParents (instance, api: DevtoolsApi, ctx: BackendCon return parents } -function initCtx (_api: DevtoolsApi, ctx: BackendContext) { +function initCtx(_api: DevtoolsApi, ctx: BackendContext) { appRecord = ctx.currentAppRecord api = _api if (!appRecord.meta) { @@ -79,7 +91,7 @@ function initCtx (_api: DevtoolsApi, ctx: BackendContext) { * traversal - e.g. if an instance is not matched, we will * recursively go deeper until a qualified child is found. */ -function findQualifiedChildrenFromList (instances: any[]): Promise { +function findQualifiedChildrenFromList(instances: any[]): Promise { instances = instances .filter(child => !isBeingDestroyed(child)) return Promise.all(!filter @@ -92,10 +104,11 @@ function findQualifiedChildrenFromList (instances: any[]): Promise { +async function findQualifiedChildren(instance): Promise { if (isQualified(instance)) { return [await capture(instance)] - } else { + } + else { let children = await findQualifiedChildrenFromList(instance.$children) // Find functional components in recursively in non-functional vnodes. @@ -113,7 +126,7 @@ async function findQualifiedChildren (instance): Promise { /** * Get children from a component instance. */ -function getInternalInstanceChildren (instance): any[] { +function getInternalInstanceChildren(instance): any[] { if (instance.$children) { return instance.$children } @@ -123,25 +136,27 @@ function getInternalInstanceChildren (instance): any[] { /** * Check if an instance is qualified. */ -function isQualified (instance): boolean { +function isQualified(instance): boolean { const name = getInstanceName(instance) - return classify(name).toLowerCase().indexOf(filter) > -1 || - kebabize(name).toLowerCase().indexOf(filter) > -1 + return classify(name).toLowerCase().includes(filter) + || kebabize(name).toLowerCase().includes(filter) } -function flatten (items: any[]): T[] { +function flatten(items: any[]): T[] { const r = items.reduce((acc, item) => { if (Array.isArray(item)) { let children = [] for (const i of item) { if (Array.isArray(i)) { children = children.concat(flatten(i)) - } else { + } + else { children.push(i) } } acc.push(...children) - } else if (item) { + } + else if (item) { acc.push(item) } @@ -150,12 +165,16 @@ function flatten (items: any[]): T[] { return r } -function captureChild (child): Promise { +function captureChild(child): Promise { if (child.fnContext && !child.componentInstance) { return capture(child) - } else if (child.componentInstance) { - if (!isBeingDestroyed(child.componentInstance)) return capture(child.componentInstance) - } else if (child.children) { + } + else if (child.componentInstance) { + if (!isBeingDestroyed(child.componentInstance)) { + return capture(child.componentInstance) + } + } + else if (child.children) { return Promise.all(flatten>(child.children.map(captureChild))) } } @@ -163,7 +182,7 @@ function captureChild (child): Promise /** * Capture the meta information of an instance. (recursive) */ -async function capture (instance, index?: number, list?: any[]): Promise { +async function capture(instance, _index?: number, _list?: any[]): Promise { if (instance.__VUE_DEVTOOLS_FUNCTIONAL_LEGACY__) { instance = instance.vnode } @@ -172,7 +191,9 @@ async function capture (instance, index?: number, list?: any[]): Promise -1 ? '$vm' + consoleId : null + ret.consoleId = consoleId > -1 ? `$vm${consoleId}` : null // check router view const isRouterView2 = instance.$vnode?.data?.routerView @@ -297,15 +321,15 @@ async function capture (instance, index?: number, list?: any[]): Promise { instanceMap.delete(refId) }) applyPerfHooks(api, instance, appRecord.options.app) @@ -335,11 +359,11 @@ function mark (instance) { } } -function markFunctional (id, vnode) { +function markFunctional(id, vnode) { const refId = vnode.fnContext.__VUE_DEVTOOLS_UID__ if (!functionalVnodeMap.has(refId)) { functionalVnodeMap.set(refId, {}) - vnode.fnContext.$on('hook:beforeDestroy', function () { + vnode.fnContext.$on('hook:beforeDestroy', () => { functionalVnodeMap.delete(refId) }) } diff --git a/packages/app-backend-vue2/src/components/update-tracking.ts b/packages/app-backend-vue2/src/components/update-tracking.ts index 15549d33c..98264ca97 100644 --- a/packages/app-backend-vue2/src/components/update-tracking.ts +++ b/packages/app-backend-vue2/src/components/update-tracking.ts @@ -1,12 +1,12 @@ -import { DevtoolsApi } from '@vue-devtools/app-backend-api' +import type { DevtoolsApi } from '@vue-devtools/app-backend-api' import { HookEvents, SharedData } from '@vue-devtools/shared-utils' import throttle from 'lodash/throttle' import { getUniqueId } from './util.js' -export function initUpdateTracking (api: DevtoolsApi, Vue) { +export function initUpdateTracking(api: DevtoolsApi, Vue) { // Global mixin Vue.mixin({ - beforeCreate () { + beforeCreate() { applyTrackingUpdateHook(api, this) }, }) @@ -17,8 +17,10 @@ const COMPONENT_HOOKS = [ 'updated', ] -export function applyTrackingUpdateHook (api: DevtoolsApi, vm) { - if (vm.$options.$_devtoolsUpdateTrackingHooks) return +export function applyTrackingUpdateHook(api: DevtoolsApi, vm) { + if (vm.$options.$_devtoolsUpdateTrackingHooks) { + return + } vm.$options.$_devtoolsUpdateTrackingHooks = true const handler = throttle(async function (this: any) { @@ -39,9 +41,11 @@ export function applyTrackingUpdateHook (api: DevtoolsApi, vm) { const currentValue = vm.$options[hook] if (Array.isArray(currentValue)) { vm.$options[hook] = [handler, ...currentValue] - } else if (typeof currentValue === 'function') { + } + else if (typeof currentValue === 'function') { vm.$options[hook] = [handler, currentValue] - } else { + } + else { vm.$options[hook] = [handler] } } diff --git a/packages/app-backend-vue2/src/components/util.ts b/packages/app-backend-vue2/src/components/util.ts index 61eaea9d2..5d3cd6e9c 100644 --- a/packages/app-backend-vue2/src/components/util.ts +++ b/packages/app-backend-vue2/src/components/util.ts @@ -1,31 +1,38 @@ import { getComponentName } from '@vue-devtools/shared-utils' -import { AppRecord } from '@vue-devtools/app-backend-api' +import type { AppRecord } from '@vue-devtools/app-backend-api' -export function isBeingDestroyed (instance) { +export function isBeingDestroyed(instance) { return instance._isBeingDestroyed } /** * Get the appropriate display name for an instance. */ -export function getInstanceName (instance) { +export function getInstanceName(instance) { const name = getComponentName(instance.$options || instance.fnOptions || {}) - if (name) return name + if (name) { + return name + } return instance.$root === instance ? 'Root' : 'Anonymous Component' } -export function getRenderKey (value): string { - if (value == null) return +export function getRenderKey(value): string { + if (value == null) { + return + } const type = typeof value if (type === 'number') { return value.toString() - } else if (type === 'string') { + } + else if (type === 'string') { return `'${value}'` - } else if (Array.isArray(value)) { + } + else if (Array.isArray(value)) { return 'Array' - } else { + } + else { return 'Object' } } @@ -33,8 +40,10 @@ export function getRenderKey (value): string { /** * Returns a devtools unique id for instance. */ -export function getUniqueId (instance, appRecord?: AppRecord): string { - if (instance.__VUE_DEVTOOLS_UID__ != null) return instance.__VUE_DEVTOOLS_UID__ +export function getUniqueId(instance, appRecord?: AppRecord): string { + if (instance.__VUE_DEVTOOLS_UID__ != null) { + return instance.__VUE_DEVTOOLS_UID__ + } let rootVueId = instance.$root.__VUE_DEVTOOLS_APP_RECORD_ID__ if (!rootVueId && appRecord) { rootVueId = appRecord.id diff --git a/packages/app-backend-vue2/src/events.ts b/packages/app-backend-vue2/src/events.ts index aa6764a75..41d1c7d57 100644 --- a/packages/app-backend-vue2/src/events.ts +++ b/packages/app-backend-vue2/src/events.ts @@ -1,9 +1,9 @@ -import { BackendContext } from '@vue-devtools/app-backend-api' +import type { BackendContext } from '@vue-devtools/app-backend-api' import { HookEvents } from '@vue-devtools/shared-utils' const internalRE = /^(?:pre-)?hook:/ -function wrap (app, Vue, method, ctx: BackendContext) { +function wrap(app, Vue, method, ctx: BackendContext) { const original = Vue.prototype[method] if (original) { Vue.prototype[method] = function (...args) { @@ -13,7 +13,7 @@ function wrap (app, Vue, method, ctx: BackendContext) { } } - function logEvent (vm, type, eventName, payload) { + function logEvent(vm, type, eventName, payload) { // The string check is important for compat with 1.x where the first // argument may be an object instead of a string. // this also ensures the event is only logged for direct $emit (source) @@ -25,8 +25,8 @@ function wrap (app, Vue, method, ctx: BackendContext) { } } -export function wrapVueForEvents (app, Vue, ctx: BackendContext) { - ['$emit', '$broadcast', '$dispatch'].forEach(method => { +export function wrapVueForEvents(app, Vue, ctx: BackendContext) { + ['$emit', '$broadcast', '$dispatch'].forEach((method) => { wrap(app, Vue, method, ctx) }) } diff --git a/packages/app-backend-vue2/src/index.ts b/packages/app-backend-vue2/src/index.ts index 5eba55661..32aba118e 100644 --- a/packages/app-backend-vue2/src/index.ts +++ b/packages/app-backend-vue2/src/index.ts @@ -1,10 +1,10 @@ -import { defineBackend, BuiltinBackendFeature } from '@vue-devtools/app-backend-api' +import { BuiltinBackendFeature, defineBackend } from '@vue-devtools/app-backend-api' import { backendInjections, getComponentName } from '@vue-devtools/shared-utils' -import { ComponentInstance } from '@vue/devtools-api' +import type { ComponentInstance } from '@vue/devtools-api' import { editState, getCustomInstanceDetails, getInstanceDetails } from './components/data' -import { getInstanceOrVnodeRect, findRelatedComponent, getRootElementsFromComponentInstance } from './components/el' +import { findRelatedComponent, getInstanceOrVnodeRect, getRootElementsFromComponentInstance } from './components/el' import { initPerf } from './components/perf.js' -import { getComponentParents, instanceMap, walkTree } from './components/tree' +import { getComponentParents, getInstanceMap, walkTree } from './components/tree' import { initUpdateTracking } from './components/update-tracking.js' import { getInstanceName } from './components/util' import { wrapVueForEvents } from './events' @@ -15,16 +15,17 @@ export const backend = defineBackend({ features: [ BuiltinBackendFeature.FLUSH, ], - setup (api) { - api.on.getAppRecordName(payload => { + setup(api) { + api.on.getAppRecordName((payload) => { if (payload.app.name) { payload.name = payload.app.name - } else if (payload.app.$options.name) { + } + else if (payload.app.$options.name) { payload.name = payload.app.$options.name } }) - api.on.getAppRootInstance(payload => { + api.on.getAppRootInstance((payload) => { payload.root = payload.app as unknown as ComponentInstance }) @@ -36,37 +37,37 @@ export const backend = defineBackend({ payload.parentInstances = getComponentParents(payload.componentInstance, api, ctx) }) - api.on.inspectComponent(payload => { + api.on.inspectComponent((payload) => { injectToUtils() payload.instanceData = getInstanceDetails(payload.componentInstance) }) - api.on.getComponentBounds(payload => { + api.on.getComponentBounds((payload) => { payload.bounds = getInstanceOrVnodeRect(payload.componentInstance) }) - api.on.getComponentName(payload => { + api.on.getComponentName((payload) => { const instance = payload.componentInstance payload.name = instance.fnContext ? getComponentName(instance.fnOptions) : getInstanceName(instance) }) - api.on.getElementComponent(payload => { + api.on.getElementComponent((payload) => { payload.componentInstance = findRelatedComponent(payload.element) }) - api.on.editComponentState(payload => { + api.on.editComponentState((payload) => { editState(payload, api.stateEditor) }) - api.on.getComponentRootElements(payload => { + api.on.getComponentRootElements((payload) => { payload.rootElements = getRootElementsFromComponentInstance(payload.componentInstance) }) - api.on.getComponentDevtoolsOptions(payload => { + api.on.getComponentDevtoolsOptions((payload) => { payload.options = payload.componentInstance.$options.devtools }) - api.on.getComponentRenderCode(payload => { + api.on.getComponentRenderCode((payload) => { payload.code = payload.componentInstance.$options.render.toString() }) @@ -75,15 +76,19 @@ export const backend = defineBackend({ }) }, - setupApp (api, appRecord) { + setupApp(api, appRecord) { const { Vue } = appRecord.options.meta const app = appRecord.options.app // State editor overrides - api.stateEditor.createDefaultSetCallback = state => { + api.stateEditor.createDefaultSetCallback = (state) => { return (obj, field, value) => { - if (state.remove || state.newKey) Vue.delete(obj, field) - if (!state.remove) Vue.set(obj, state.newKey || field, value) + if (state.remove || state.newKey) { + Vue.delete(obj, field) + } + if (!state.remove) { + Vue.set(obj, state.newKey || field, value) + } } } @@ -102,9 +107,9 @@ export const backend = defineBackend({ }) // @TODO refactor -function injectToUtils () { +function injectToUtils() { backendInjections.getCustomInstanceDetails = getCustomInstanceDetails backendInjections.getCustomObjectDetails = () => undefined - backendInjections.instanceMap = instanceMap + backendInjections.instanceMap = getInstanceMap() backendInjections.isVueInstance = val => val._isVue } diff --git a/packages/app-backend-vue2/src/plugin.ts b/packages/app-backend-vue2/src/plugin.ts index ade044fa0..40a4b2444 100644 --- a/packages/app-backend-vue2/src/plugin.ts +++ b/packages/app-backend-vue2/src/plugin.ts @@ -1,11 +1,26 @@ -import { DevtoolsApi } from '@vue-devtools/app-backend-api' -import { App, ComponentState, CustomInspectorNode, CustomInspectorState, setupDevtoolsPlugin } from '@vue/devtools-api' +import type { DevtoolsApi } from '@vue-devtools/app-backend-api' +import type { App, ComponentState, CustomInspectorNode, CustomInspectorState } from '@vue/devtools-api' +import { setupDevtoolsPlugin } from '@vue/devtools-api' import { isEmptyObject, target } from '@vue-devtools/shared-utils' import copy from 'clone-deep' let actionId = 0 -export function setupPlugin (api: DevtoolsApi, app: App, Vue) { +const VUEX_ROOT_PATH = '__vdt_root' +const VUEX_MODULE_PATH_SEPARATOR = '[vdt]' +const VUEX_MODULE_PATH_SEPARATOR_REG = /\[vdt\]/g + +/** + * Extracted from tailwind palette + */ +const BLUE_600 = 0x2563EB +const LIME_500 = 0x84CC16 +const CYAN_400 = 0x22D3EE +const ORANGE_400 = 0xFB923C +const WHITE = 0xFFFFFF +const DARK = 0x666666 + +export function setupPlugin(api: DevtoolsApi, app: App, Vue) { const ROUTER_INSPECTOR_ID = 'vue2-router-inspector' const ROUTER_CHANGES_LAYER_ID = 'vue2-router-changes' @@ -27,7 +42,7 @@ export function setupPlugin (api: DevtoolsApi, app: App, Vue) { defaultValue: false, }, }, - }, api => { + }, (api) => { const hook = target.__VUE_DEVTOOLS_GLOBAL_HOOK__ // Vue Router @@ -43,17 +58,18 @@ export function setupPlugin (api: DevtoolsApi, app: App, Vue) { treeFilterPlaceholder: 'Search routes', }) - api.on.getInspectorTree(payload => { + api.on.getInspectorTree((payload) => { if (payload.inspectorId === ROUTER_INSPECTOR_ID) { if (router.options.routes) { payload.rootNodes = router.options.routes.map(route => formatRouteNode(router, route, '', payload.filter)).filter(Boolean) - } else { + } + else { console.warn(`[Vue Devtools] No routes found in router`, router.options) } } }) - api.on.getInspectorState(payload => { + api.on.getInspectorState((payload) => { if (payload.inspectorId === ROUTER_INSPECTOR_ID) { const route = router.matcher.getRoutes().find(r => getPathId(r) === payload.nodeId) if (route) { @@ -69,7 +85,7 @@ export function setupPlugin (api: DevtoolsApi, app: App, Vue) { api.addTimelineLayer({ id: ROUTER_CHANGES_LAYER_ID, label: 'Router Navigations', - color: 0x40a8c4, + color: 0x40A8C4, }) router.afterEach((to, from) => { @@ -105,7 +121,8 @@ export function setupPlugin (api: DevtoolsApi, app: App, Vue) { const nodes = [] flattenStoreForInspectorTree(nodes, store._modules.root, payload.filter, '') payload.rootNodes = nodes - } else { + } + else { payload.rootNodes = [ formatStoreForInspectorTree(store._modules.root, 'Root', ''), ] @@ -179,7 +196,7 @@ export function setupPlugin (api: DevtoolsApi, app: App, Vue) { }) }) - function legacySingleActionSub (action, state) { + function legacySingleActionSub(action, state) { const data: any = {} if (action.payload) { data.payload = action.payload @@ -250,7 +267,7 @@ export function setupPlugin (api: DevtoolsApi, app: App, Vue) { }, { prepend: true }) // Inspect getters on mutations - api.on.inspectTimelineEvent(payload => { + api.on.inspectTimelineEvent((payload) => { if (payload.layerId === VUEX_MUTATIONS_ID) { const getterKeys = Object.keys(store.getters) if (getterKeys.length) { @@ -275,17 +292,7 @@ export function setupPlugin (api: DevtoolsApi, app: App, Vue) { }) } -/** - * Extracted from tailwind palette - */ -const BLUE_600 = 0x2563eb -const LIME_500 = 0x84cc16 -const CYAN_400 = 0x22d3ee -const ORANGE_400 = 0xfb923c -const WHITE = 0xffffff -const DARK = 0x666666 - -function formatRouteNode (router, route, parentPath: string, filter: string): CustomInspectorNode { +function formatRouteNode(router, route, parentPath: string, filter: string): CustomInspectorNode { const node: CustomInspectorNode = { id: route.path.startsWith('/') ? route.path : `${parentPath}/${route.path}`, label: route.path, @@ -293,7 +300,9 @@ function formatRouteNode (router, route, parentPath: string, filter: string): Cu tags: [], } - if (filter && !node.id.includes(filter) && !node.children?.length) return null + if (filter && !node.id.includes(filter) && !node.children?.length) { + return null + } if (route.name != null) { node.tags.push({ @@ -322,8 +331,8 @@ function formatRouteNode (router, route, parentPath: string, filter: string): Cu if (route.redirect) { node.tags.push({ label: - 'redirect: ' + - (typeof route.redirect === 'string' ? route.redirect : 'Object'), + `redirect: ${ + typeof route.redirect === 'string' ? route.redirect : 'Object'}`, textColor: WHITE, backgroundColor: DARK, }) @@ -332,7 +341,7 @@ function formatRouteNode (router, route, parentPath: string, filter: string): Cu return node } -function formatRouteData (route) { +function formatRouteData(route) { const data: Omit[] = [] data.push({ key: 'path', value: route.path }) @@ -372,7 +381,7 @@ function formatRouteData (route) { return data } -function getPathId (routeMatcher) { +function getPathId(routeMatcher) { let path = routeMatcher.path if (routeMatcher.parent) { path = getPathId(routeMatcher.parent) + path @@ -386,11 +395,7 @@ const TAG_NAMESPACED = { backgroundColor: DARK, } -const VUEX_ROOT_PATH = '__vdt_root' -const VUEX_MODULE_PATH_SEPARATOR = '[vdt]' -const VUEX_MODULE_PATH_SEPARATOR_REG = /\[vdt\]/g - -function formatStoreForInspectorTree (module, moduleName: string, path: string): CustomInspectorNode { +function formatStoreForInspectorTree(module, moduleName: string, path: string): CustomInspectorNode { return { id: path || VUEX_ROOT_PATH, // all modules end with a `/`, we want the last segment only @@ -398,7 +403,7 @@ function formatStoreForInspectorTree (module, moduleName: string, path: string): // nested/cart/ -> cart label: moduleName, tags: module.namespaced ? [TAG_NAMESPACED] : [], - children: Object.keys(module._children ?? {}).map((key) => + children: Object.keys(module._children ?? {}).map(key => formatStoreForInspectorTree( module._children[key], key, @@ -408,7 +413,7 @@ function formatStoreForInspectorTree (module, moduleName: string, path: string): } } -function flattenStoreForInspectorTree (result: CustomInspectorNode[], module, filter: string, path: string) { +function flattenStoreForInspectorTree(result: CustomInspectorNode[], module, filter: string, path: string) { if (path.includes(filter)) { result.push({ id: path || VUEX_ROOT_PATH, @@ -416,18 +421,18 @@ function flattenStoreForInspectorTree (result: CustomInspectorNode[], module, fi tags: module.namespaced ? [TAG_NAMESPACED] : [], }) } - Object.keys(module._children).forEach(moduleName => { + Object.keys(module._children).forEach((moduleName) => { flattenStoreForInspectorTree(result, module._children[moduleName], filter, path + moduleName + VUEX_MODULE_PATH_SEPARATOR) }) } -function extractNameFromPath (path: string) { +function extractNameFromPath(path: string) { return path && path !== VUEX_ROOT_PATH ? path.split(VUEX_MODULE_PATH_SEPARATOR).slice(-2, -1)[0] : 'Root' } -function formatStoreForInspectorState (module, getters, path): CustomInspectorState { +function formatStoreForInspectorState(module, getters, path): CustomInspectorState { const storeState: CustomInspectorState = { - state: Object.keys(module.context.state ?? {}).map((key) => ({ + state: Object.keys(module.context.state ?? {}).map(key => ({ key, editable: true, value: module.context.state[key], @@ -452,11 +457,12 @@ function formatStoreForInspectorState (module, getters, path): CustomInspectorSt for (const key of gettersKeys) { moduleGetters[key] = canThrow(() => getters[key]) } - } else { + } + else { moduleGetters = getters } const tree = transformPathsToObjectTree(moduleGetters) - storeState.getters = Object.keys(tree).map((key) => ({ + storeState.getters = Object.keys(tree).map(key => ({ key: key.endsWith('/') ? extractNameFromPath(key) : key, editable: false, value: canThrow(() => tree[key]), @@ -467,9 +473,9 @@ function formatStoreForInspectorState (module, getters, path): CustomInspectorSt return storeState } -function transformPathsToObjectTree (getters) { +function transformPathsToObjectTree(getters) { const result = {} - Object.keys(getters).forEach(key => { + Object.keys(getters).forEach((key) => { const path = key.split('/') if (path.length > 1) { let target = result @@ -488,19 +494,22 @@ function transformPathsToObjectTree (getters) { target = target[p]._custom.value } target[leafKey] = canThrow(() => getters[key]) - } else { + } + else { result[key] = canThrow(() => getters[key]) } }) return result } -function getStoreModule (moduleMap, path) { - const names = path.split(VUEX_MODULE_PATH_SEPARATOR).filter((n) => n) +function getStoreModule(moduleMap, path) { + const names = path.split(VUEX_MODULE_PATH_SEPARATOR).filter(n => n) return names.reduce( ({ module, getterPath }, moduleName, i) => { const child = module[moduleName === VUEX_ROOT_PATH ? 'root' : moduleName] - if (!child) return null + if (!child) { + return null + } return { module: i === names.length - 1 ? child : child._children, getterPath: child._rawModule.namespaced @@ -515,10 +524,11 @@ function getStoreModule (moduleMap, path) { ) } -function canThrow (cb: () => any) { +function canThrow(cb: () => any) { try { return cb() - } catch (e) { + } + catch (e) { return e } } diff --git a/packages/app-backend-vue2/tsconfig.json b/packages/app-backend-vue2/tsconfig.json index f565bc660..becb763b2 100644 --- a/packages/app-backend-vue2/tsconfig.json +++ b/packages/app-backend-vue2/tsconfig.json @@ -3,25 +3,25 @@ "target": "ES2019", "module": "commonjs", "moduleResolution": "node", - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, "resolveJsonModule": true, - "skipLibCheck": true, "types": [ "node", "webpack-env" ], - "sourceMap": true, - "preserveWatchOutput": true, + "strictBindCallApply": true, + "strictFunctionTypes": true, + "alwaysStrict": true, // Strict "noImplicitAny": false, "noImplicitThis": true, - "alwaysStrict": true, - "strictBindCallApply": true, - "strictFunctionTypes": true, + "sourceMap": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "skipLibCheck": true, + "preserveWatchOutput": true }, "include": [ - "src/**/*", + "src/**/*" ], "exclude": [ "node_modules" diff --git a/packages/app-backend-vue3/package.json b/packages/app-backend-vue3/package.json index 279544d59..10e2e8a03 100644 --- a/packages/app-backend-vue3/package.json +++ b/packages/app-backend-vue3/package.json @@ -10,13 +10,13 @@ "ts": "tsc -d -outDir lib" }, "dependencies": { - "@vue/devtools-api": "^6.0.0-beta.1", "@vue-devtools/app-backend-api": "^0.0.0", - "@vue-devtools/shared-utils": "^0.0.0" + "@vue-devtools/shared-utils": "^0.0.0", + "@vue/devtools-api": "^6.0.0-beta.1" }, "devDependencies": { - "@types/node": "^13.9.1", + "@types/node": "^20.11.16", "@types/webpack-env": "^1.15.1", - "typescript": "^4.5.2" + "typescript": "^5.3.3" } } diff --git a/packages/app-backend-vue3/src/components/data.ts b/packages/app-backend-vue3/src/components/data.ts index 9b3577fed..9aa717598 100644 --- a/packages/app-backend-vue3/src/components/data.ts +++ b/packages/app-backend-vue3/src/components/data.ts @@ -1,8 +1,9 @@ -import { BackendContext } from '@vue-devtools/app-backend-api' -import { getInstanceName, getUniqueComponentId } from './util' -import { camelize, StateEditor, SharedData, kebabize } from '@vue-devtools/shared-utils' -import { ComponentInstance, CustomState, HookPayloads, Hooks, InspectedComponentData } from '@vue/devtools-api' +import type { BackendContext } from '@vue-devtools/app-backend-api' +import type { StateEditor } from '@vue-devtools/shared-utils' +import { SharedData, camelize, kebabize } from '@vue-devtools/shared-utils' +import type { ComponentInstance, CustomState, HookPayloads, Hooks, InspectedComponentData } from '@vue/devtools-api' import { returnError } from '../util' +import { getInstanceName, getUniqueComponentId } from './util' const vueBuiltins = [ 'nextTick', @@ -61,7 +62,7 @@ const vueBuiltins = [ /** * Get the detailed information of an inspected instance. */ -export function getInstanceDetails (instance: any, ctx: BackendContext): InspectedComponentData { +export function getInstanceDetails(instance: any, ctx: BackendContext): InspectedComponentData { return { id: getUniqueComponentId(instance, ctx), name: getInstanceName(instance), @@ -70,7 +71,7 @@ export function getInstanceDetails (instance: any, ctx: BackendContext): Inspect } } -function getInstanceState (instance) { +function getInstanceState(instance) { const mergedType = resolveMergedOptions(instance) return processProps(instance).concat( processState(instance), @@ -92,7 +93,7 @@ function getInstanceState (instance) { * @param {Vue} instance * @return {Array} */ -function processProps (instance) { +function processProps(instance) { const propsData = [] const propDefinitions = instance.type.props @@ -126,7 +127,7 @@ const fnTypeRE = /^(?:function|class) (\w+)/ /** * Convert prop type constructor to string. */ -function getPropType (type) { +function getPropType(type) { if (Array.isArray(type)) { return type.map(t => getPropType(t)).join(' or ') } @@ -148,12 +149,12 @@ function getPropType (type) { * @return {Array} */ -function processState (instance) { +function processState(instance) { const type = instance.type const props = type.props - const getters = - type.vuex && - type.vuex.getters + const getters + = type.vuex + && type.vuex.getters const computedDefs = type.computed const data = { @@ -163,9 +164,9 @@ function processState (instance) { return Object.keys(data) .filter(key => ( - !(props && key in props) && - !(getters && key in getters) && - !(computedDefs && key in computedDefs) + !(props && key in props) + && !(getters && key in getters) + && !(computedDefs && key in computedDefs) )) .map(key => ({ key, @@ -175,7 +176,7 @@ function processState (instance) { })) } -function processSetupState (instance) { +function processSetupState(instance) { const raw = instance.devtoolsRawSetupState const combinedSetupState = (Object.keys(instance.setupState).length ? instance.setupState @@ -184,17 +185,17 @@ function processSetupState (instance) { return Object.keys(combinedSetupState) .filter(key => !vueBuiltins.includes(key) && key.split(/(?=[A-Z])/)[0] !== 'use') - .map(key => { + .map((key) => { const value = returnError(() => toRaw(combinedSetupState[key])) const rawData = raw[key] let result: any - let isOther = typeof value === 'function' || - typeof value?.render === 'function' || - typeof value?.__asyncLoader === 'function' || - typeof value === 'object' && ('setup' in value || 'props' in value) + let isOther = typeof value === 'function' + || typeof value?.render === 'function' + || typeof value?.__asyncLoader === 'function' + || (typeof value === 'object' && ('setup' in value || 'props' in value)) if (rawData) { const info = getSetupStateInfo(rawData) @@ -225,30 +226,30 @@ function processSetupState (instance) { }) } -function isRef (raw: any): boolean { +function isRef(raw: any): boolean { return !!raw.__v_isRef } -function isComputed (raw: any): boolean { +function isComputed(raw: any): boolean { return isRef(raw) && !!raw.effect } -function isReactive (raw: any): boolean { +function isReactive(raw: any): boolean { return !!raw.__v_isReactive } -function isReadOnly (raw: any): boolean { +function isReadOnly(raw: any): boolean { return !!raw.__v_isReadonly } -function toRaw (value: any) { +function toRaw(value: any) { if (value?.__v_raw) { return value.__v_raw } return value } -function getSetupStateInfo (raw: any) { +function getSetupStateInfo(raw: any) { return { ref: isRef(raw), computed: isComputed(raw), @@ -257,7 +258,7 @@ function getSetupStateInfo (raw: any) { } } -export function getCustomObjectDetails (object: any, proto: string): CustomState | undefined { +export function getCustomObjectDetails(object: any, _proto: string): CustomState | undefined { const info = getSetupStateInfo(object) const isState = info.ref || info.computed || info.reactive @@ -291,7 +292,7 @@ export function getCustomObjectDetails (object: any, proto: string): CustomState * @param {Vue} instance * @return {Array} */ -function processComputed (instance, mergedType) { +function processComputed(instance, mergedType) { const type = mergedType const computed = [] const defs = type.computed || {} @@ -315,7 +316,7 @@ function processComputed (instance, mergedType) { return computed } -function processAttrs (instance) { +function processAttrs(instance) { return Object.keys(instance.attrs) .map(key => ({ type: 'attrs', @@ -324,7 +325,7 @@ function processAttrs (instance) { })) } -function processProvide (instance) { +function processProvide(instance) { return Reflect.ownKeys(instance.provides) .map(key => ({ type: 'provided', @@ -333,8 +334,10 @@ function processProvide (instance) { })) } -function processInject (instance, mergedType) { - if (!mergedType?.inject) return [] +function processInject(instance, mergedType) { + if (!mergedType?.inject) { + return [] + } let keys = [] let defaultValue if (Array.isArray(mergedType.inject)) { @@ -342,13 +345,15 @@ function processInject (instance, mergedType) { key, originalKey: key, })) - } else { - keys = Reflect.ownKeys(mergedType.inject).map(key => { + } + else { + keys = Reflect.ownKeys(mergedType.inject).map((key) => { const value = mergedType.inject[key] let originalKey if (typeof value === 'string' || typeof value === 'symbol') { originalKey = value - } else { + } + else { originalKey = value.from defaultValue = value.default } @@ -361,11 +366,11 @@ function processInject (instance, mergedType) { return keys.map(({ key, originalKey }) => ({ type: 'injected', key: originalKey && key !== originalKey ? `${originalKey.toString()} ➞ ${key.toString()}` : key.toString(), - value: returnError(() => instance.ctx.hasOwnProperty(key) ? instance.ctx[key] : instance.provides.hasOwnProperty(originalKey) ? instance.provides[originalKey] : defaultValue), + value: returnError(() => Object.prototype.hasOwnProperty.call(instance.ctx, key) ? instance.ctx[key] : Object.prototype.hasOwnProperty.call(instance.provides, originalKey) ? instance.provides[originalKey] : defaultValue), })) } -function processRefs (instance) { +function processRefs(instance) { return Object.keys(instance.refs) .map(key => ({ type: 'refs', @@ -374,7 +379,7 @@ function processRefs (instance) { })) } -function processEventListeners (instance) { +function processEventListeners(instance) { const emitsDefinition = instance.type.emits const declaredEmits = Array.isArray(emitsDefinition) ? emitsDefinition : Object.keys(emitsDefinition ?? {}) const declaredEmitsMap = declaredEmits.reduce((emitsMap, key) => { @@ -403,24 +408,30 @@ function processEventListeners (instance) { return result } -export function editState ({ componentInstance, path, state, type }: HookPayloads[Hooks.EDIT_COMPONENT_STATE], stateEditor: StateEditor, ctx: BackendContext) { - if (!['data', 'props', 'computed', 'setup'].includes(type)) return +export function editState({ componentInstance, path, state, type }: HookPayloads[Hooks.EDIT_COMPONENT_STATE], stateEditor: StateEditor, _ctx: BackendContext) { + if (!['data', 'props', 'computed', 'setup'].includes(type)) { + return + } let target: any const targetPath: string[] = path.slice() if (Object.keys(componentInstance.props).includes(path[0])) { // Props target = componentInstance.props - } else if (componentInstance.devtoolsRawSetupState && Object.keys(componentInstance.devtoolsRawSetupState).includes(path[0])) { + } + else if (componentInstance.devtoolsRawSetupState && Object.keys(componentInstance.devtoolsRawSetupState).includes(path[0])) { // Setup target = componentInstance.devtoolsRawSetupState const currentValue = stateEditor.get(componentInstance.devtoolsRawSetupState, path) if (currentValue != null) { const info = getSetupStateInfo(currentValue) - if (info.readonly) return + if (info.readonly) { + return + } } - } else { + } + else { target = componentInstance.proxy } @@ -429,7 +440,7 @@ export function editState ({ componentInstance, path, state, type }: HookPayload } } -function reduceStateList (list) { +function reduceStateList(list) { if (!list.length) { return undefined } @@ -441,8 +452,10 @@ function reduceStateList (list) { }, {}) } -export function getCustomInstanceDetails (instance) { - if (instance._) instance = instance._ +export function getCustomInstanceDetails(instance) { + if (instance._) { + instance = instance._ + } const state = getInstanceState(instance) return { _custom: { @@ -458,20 +471,22 @@ export function getCustomInstanceDetails (instance) { } } -function resolveMergedOptions ( +function resolveMergedOptions( instance: ComponentInstance, ) { const raw = instance.type const { mixins, extends: extendsOptions } = raw const globalMixins = instance.appContext.mixins - if (!globalMixins.length && !mixins && !extendsOptions) return raw + if (!globalMixins.length && !mixins && !extendsOptions) { + return raw + } const options = {} globalMixins.forEach(m => mergeOptions(options, m, instance)) mergeOptions(options, raw, instance) return options } -function mergeOptions ( +function mergeOptions( to: any, from: any, instance: ComponentInstance, @@ -480,21 +495,24 @@ function mergeOptions ( from = from.options } - if (!from) return to + if (!from) { + return to + } const { mixins, extends: extendsOptions } = from extendsOptions && mergeOptions(to, extendsOptions, instance) - mixins && - mixins.forEach((m) => - mergeOptions(to, m, instance), - ) + mixins + && mixins.forEach(m => + mergeOptions(to, m, instance), + ) for (const key of ['computed', 'inject']) { if (Object.prototype.hasOwnProperty.call(from, key)) { if (!to[key]) { to[key] = from[key] - } else { + } + else { to[key] = Object.assign(Object.create(null), to[key], from[key]) } } diff --git a/packages/app-backend-vue3/src/components/el.ts b/packages/app-backend-vue3/src/components/el.ts index 6b71dd444..442a26e53 100644 --- a/packages/app-backend-vue3/src/components/el.ts +++ b/packages/app-backend-vue3/src/components/el.ts @@ -1,20 +1,24 @@ import { inDoc, isBrowser } from '@vue-devtools/shared-utils' import { isFragment } from './util' -export function getComponentInstanceFromElement (element) { +export function getComponentInstanceFromElement(element) { return element.__vueParentComponent } -export function getRootElementsFromComponentInstance (instance) { +export function getRootElementsFromComponentInstance(instance) { if (isFragment(instance)) { return getFragmentRootElements(instance.subTree) } - if (!instance.subTree) return [] + if (!instance.subTree) { + return [] + } return [instance.subTree.el] } -function getFragmentRootElements (vnode): any[] { - if (!vnode.children) return [] +function getFragmentRootElements(vnode): any[] { + if (!vnode.children) { + return [] + } const list = [] @@ -22,7 +26,8 @@ function getFragmentRootElements (vnode): any[] { const childVnode = vnode.children[i] if (childVnode.component) { list.push(...getRootElementsFromComponentInstance(childVnode.component)) - } else if (childVnode.el) { + } + else if (childVnode.el) { list.push(childVnode.el) } } @@ -34,9 +39,9 @@ function getFragmentRootElements (vnode): any[] { * Get the client rect for an instance. * * @param {Vue|Vnode} instance - * @return {Object} + * @return {object} */ -export function getInstanceOrVnodeRect (instance) { +export function getInstanceOrVnodeRect(instance) { const el = instance.subTree.el if (!isBrowser) { @@ -49,26 +54,28 @@ export function getInstanceOrVnodeRect (instance) { if (isFragment(instance)) { return addIframePosition(getFragmentRect(instance.subTree), getElWindow(el)) - } else if (el.nodeType === 1) { + } + else if (el.nodeType === 1) { return addIframePosition(el.getBoundingClientRect(), getElWindow(el)) - } else if (instance.subTree.component) { + } + else if (instance.subTree.component) { return getInstanceOrVnodeRect(instance.subTree.component) } } -function createRect () { +function createRect() { const rect = { top: 0, bottom: 0, left: 0, right: 0, - get width () { return rect.right - rect.left }, - get height () { return rect.bottom - rect.top }, + get width() { return rect.right - rect.left }, + get height() { return rect.bottom - rect.top }, } return rect } -function mergeRects (a, b) { +function mergeRects(a, b) { if (!a.top || b.top < a.top) { a.top = b.top } @@ -91,29 +98,37 @@ let range * @param {Text} node * @return {Rect} */ -function getTextRect (node) { - if (!isBrowser) return - if (!range) range = document.createRange() +function getTextRect(node) { + if (!isBrowser) { + return + } + if (!range) { + range = document.createRange() + } range.selectNode(node) return range.getBoundingClientRect() } -function getFragmentRect (vnode) { +function getFragmentRect(vnode) { const rect = createRect() - if (!vnode.children) return rect + if (!vnode.children) { + return rect + } for (let i = 0, l = vnode.children.length; i < l; i++) { const childVnode = vnode.children[i] let childRect if (childVnode.component) { childRect = getInstanceOrVnodeRect(childVnode.component) - } else if (childVnode.el) { + } + else if (childVnode.el) { const el = childVnode.el if (el.nodeType === 1 || el.getBoundingClientRect) { childRect = el.getBoundingClientRect() - } else if (el.nodeType === 3 && el.data.trim()) { + } + else if (el.nodeType === 3 && el.data.trim()) { childRect = getTextRect(el) } } @@ -125,11 +140,11 @@ function getFragmentRect (vnode) { return rect } -function getElWindow (el: HTMLElement) { +function getElWindow(el: HTMLElement) { return el.ownerDocument.defaultView } -function addIframePosition (bounds, win: any) { +function addIframePosition(bounds, win: any) { if (win.__VUE_DEVTOOLS_IFRAME__) { const rect = mergeRects(createRect(), bounds) const iframeBounds = win.__VUE_DEVTOOLS_IFRAME__.getBoundingClientRect() diff --git a/packages/app-backend-vue3/src/components/filter.ts b/packages/app-backend-vue3/src/components/filter.ts index 563fc595e..b0fb5c118 100644 --- a/packages/app-backend-vue3/src/components/filter.ts +++ b/packages/app-backend-vue3/src/components/filter.ts @@ -4,7 +4,7 @@ import { getInstanceName } from './util' export class ComponentFilter { filter: string - constructor (filter: string) { + constructor(filter: string) { this.filter = filter || '' } @@ -12,11 +12,11 @@ export class ComponentFilter { * Check if an instance is qualified. * * @param {Vue|Vnode} instance - * @return {Boolean} + * @return {boolean} */ - isQualified (instance) { + isQualified(instance) { const name = getInstanceName(instance) - return classify(name).toLowerCase().indexOf(this.filter) > -1 || - kebabize(name).toLowerCase().indexOf(this.filter) > -1 + return classify(name).toLowerCase().includes(this.filter) + || kebabize(name).toLowerCase().includes(this.filter) } } diff --git a/packages/app-backend-vue3/src/components/tree.ts b/packages/app-backend-vue3/src/components/tree.ts index 392a6a12f..283bd010e 100644 --- a/packages/app-backend-vue3/src/components/tree.ts +++ b/packages/app-backend-vue3/src/components/tree.ts @@ -1,7 +1,7 @@ -import { isBeingDestroyed, getUniqueComponentId, getInstanceName, getRenderKey, isFragment } from './util' +import type { BackendContext, DevtoolsApi } from '@vue-devtools/app-backend-api' +import type { ComponentTreeNode } from '@vue/devtools-api' +import { getInstanceName, getRenderKey, getUniqueComponentId, isBeingDestroyed, isFragment } from './util' import { ComponentFilter } from './filter' -import { BackendContext, DevtoolsApi } from '@vue-devtools/app-backend-api' -import { ComponentTreeNode } from '@vue/devtools-api' import { getRootElementsFromComponentInstance } from './el' export class ComponentWalker { @@ -14,7 +14,7 @@ export class ComponentWalker { // Some instances may be both on a component and on a child abstract/functional component captureIds: Map - constructor (maxDepth: number, filter: string, recursively: boolean, api: DevtoolsApi, ctx: BackendContext) { + constructor(maxDepth: number, filter: string, recursively: boolean, api: DevtoolsApi, ctx: BackendContext) { this.ctx = ctx this.api = api this.maxDepth = maxDepth @@ -22,16 +22,17 @@ export class ComponentWalker { this.componentFilter = new ComponentFilter(filter) } - getComponentTree (instance: any): Promise { + getComponentTree(instance: any): Promise { this.captureIds = new Map() return this.findQualifiedChildren(instance, 0) } - getComponentParents (instance: any) { + getComponentParents(instance: any) { this.captureIds = new Map() const parents = [] this.captureId(instance) let parent = instance + // eslint-disable-next-line no-cond-assign while ((parent = parent.parent)) { this.captureId(parent) parents.push(parent) @@ -47,16 +48,18 @@ export class ComponentWalker { * @param {Vue|Vnode} instance * @return {Vue|Array} */ - private async findQualifiedChildren (instance: any, depth: number): Promise { + private async findQualifiedChildren(instance: any, depth: number): Promise { if (this.componentFilter.isQualified(instance) && !instance.type.devtools?.hide) { return [await this.capture(instance, null, depth)] - } else if (instance.subTree) { + } + else if (instance.subTree) { // TODO functional components const list = this.isKeepAlive(instance) ? this.getKeepAliveCachedInstances(instance) : this.getInternalInstanceChildren(instance.subTree) return this.findQualifiedChildrenFromList(list, depth) - } else { + } + else { return [] } } @@ -70,12 +73,13 @@ export class ComponentWalker { * @param {Array} instances * @return {Array} */ - private async findQualifiedChildrenFromList (instances, depth: number): Promise { + private async findQualifiedChildrenFromList(instances, depth: number): Promise { instances = instances .filter(child => !isBeingDestroyed(child) && !child.type.devtools?.hide) if (!this.componentFilter.filter) { return Promise.all(instances.map((child, index, list) => this.capture(child, list, depth))) - } else { + } + else { return Array.prototype.concat.apply([], await Promise.all(instances.map(i => this.findQualifiedChildren(i, depth)))) } } @@ -83,19 +87,22 @@ export class ComponentWalker { /** * Get children from a component instance. */ - private getInternalInstanceChildren (subTree, suspense = null) { + private getInternalInstanceChildren(subTree, suspense = null) { const list = [] if (subTree) { if (subTree.component) { !suspense ? list.push(subTree.component) : list.push({ ...subTree.component, suspense }) - } else if (subTree.suspense) { + } + else if (subTree.suspense) { const suspenseKey = !subTree.suspense.isInFallback ? 'suspense default' : 'suspense fallback' list.push(...this.getInternalInstanceChildren(subTree.suspense.activeBranch, { ...subTree.suspense, suspenseKey })) - } else if (Array.isArray(subTree.children)) { - subTree.children.forEach(childSubTree => { + } + else if (Array.isArray(subTree.children)) { + subTree.children.forEach((childSubTree) => { if (childSubTree.component) { !suspense ? list.push(childSubTree.component) : list.push({ ...childSubTree.component, suspense }) - } else { + } + else { list.push(...this.getInternalInstanceChildren(childSubTree, suspense)) } }) @@ -104,8 +111,10 @@ export class ComponentWalker { return list.filter(child => !isBeingDestroyed(child) && !child.type.devtools?.hide) } - private captureId (instance): string { - if (!instance) return null + private captureId(instance): string { + if (!instance) { + return null + } // instance.uid is not reliable in devtools as there // may be 2 roots with same uid which causes unexpected @@ -116,7 +125,8 @@ export class ComponentWalker { // Dedupe if (this.captureIds.has(id)) { return - } else { + } + else { this.captureIds.set(id, undefined) } @@ -129,10 +139,12 @@ export class ComponentWalker { * Capture the meta information of an instance. (recursive) * * @param {Vue} instance - * @return {Object} + * @return {object} */ - private async capture (instance: any, list: any[], depth: number): Promise { - if (!instance) return null + private async capture(instance: any, list: any[], depth: number): Promise { + if (!instance) { + return null + } const id = this.captureId(instance) @@ -160,7 +172,7 @@ export class ComponentWalker { { label: 'functional', textColor: 0x555555, - backgroundColor: 0xeeeeee, + backgroundColor: 0xEEEEEE, }, ], autoOpen: this.recursively, @@ -200,15 +212,16 @@ export class ComponentWalker { el = el.parentElement } while (el.parentElement && parentRootElements.length && !parentRootElements.includes(el)) treeNode.domOrder = indexList.reverse() - } else { + } + else { treeNode.domOrder = [-1] } if (instance.suspense?.suspenseKey) { treeNode.tags.push({ label: instance.suspense.suspenseKey, - backgroundColor: 0xe492e4, - textColor: 0xffffff, + backgroundColor: 0xE492E4, + textColor: 0xFFFFFF, }) // update instanceMap this.mark(instance, true) @@ -222,18 +235,18 @@ export class ComponentWalker { * * @param {Vue} instance */ - private mark (instance, force = false) { + private mark(instance, force = false) { const instanceMap = this.ctx.currentAppRecord.instanceMap if (force || !instanceMap.has(instance.__VUE_DEVTOOLS_UID__)) { instanceMap.set(instance.__VUE_DEVTOOLS_UID__, instance) } } - private isKeepAlive (instance) { + private isKeepAlive(instance) { return instance.type.__isKeepAlive && instance.__v_cache } - private getKeepAliveCachedInstances (instance) { + private getKeepAliveCachedInstances(instance) { return Array.from(instance.__v_cache.values()).map((vnode: any) => vnode.component).filter(Boolean) } } diff --git a/packages/app-backend-vue3/src/components/util.ts b/packages/app-backend-vue3/src/components/util.ts index 11edccc79..4fb1b0d3c 100644 --- a/packages/app-backend-vue3/src/components/util.ts +++ b/packages/app-backend-vue3/src/components/util.ts @@ -1,18 +1,18 @@ -import { classify, basename } from '@vue-devtools/shared-utils' -import { ComponentInstance, App } from '@vue/devtools-api' -import { BackendContext } from '@vue-devtools/app-backend-api' +import { basename, classify } from '@vue-devtools/shared-utils' +import type { App, ComponentInstance } from '@vue/devtools-api' +import type { BackendContext } from '@vue-devtools/app-backend-api' -export function isBeingDestroyed (instance) { +export function isBeingDestroyed(instance) { return instance._isBeingDestroyed || instance.isUnmounted } -export function getAppRecord (instance) { +export function getAppRecord(instance) { if (instance.root) { return instance.appContext.app.__VUE_DEVTOOLS_APP_RECORD__ } } -export function isFragment (instance) { +export function isFragment(instance) { const appRecord = getAppRecord(instance) if (appRecord) { return appRecord.options.types.Fragment === instance.subTree?.type @@ -23,17 +23,25 @@ export function isFragment (instance) { * Get the appropriate display name for an instance. * * @param {Vue} instance - * @return {String} + * @return {string} */ -export function getInstanceName (instance) { +export function getInstanceName(instance) { const name = getComponentTypeName(instance.type || {}) - if (name) return name - if (instance.root === instance) return 'Root' + if (name) { + return name + } + if (instance.root === instance) { + return 'Root' + } for (const key in instance.parent?.type?.components) { - if (instance.parent.type.components[key] === instance.type) return saveComponentName(instance, key) + if (instance.parent.type.components[key] === instance.type) { + return saveComponentName(instance, key) + } } for (const key in instance.appContext?.components) { - if (instance.appContext.components[key] === instance.type) return saveComponentName(instance, key) + if (instance.appContext.components[key] === instance.type) { + return saveComponentName(instance, key) + } } const fileName = getComponentFileName(instance.type || {}) if (fileName) { @@ -42,16 +50,16 @@ export function getInstanceName (instance) { return 'Anonymous Component' } -function saveComponentName (instance, key) { +function saveComponentName(instance, key) { instance.type.__vdevtools_guessedName = key return key } -function getComponentTypeName (options) { +function getComponentTypeName(options) { return options.name || options._componentTag || options.__vdevtools_guessedName || options.__name } -function getComponentFileName (options) { +function getComponentFileName(options) { const file = options.__file // injected by vue-loader if (file) { return classify(basename(file, '.vue')) @@ -62,30 +70,35 @@ function getComponentFileName (options) { * Returns a devtools unique id for instance. * @param {Vue} instance */ -export function getUniqueComponentId (instance, ctx: BackendContext) { +export function getUniqueComponentId(instance, _ctx: BackendContext) { const appId = instance.appContext.app.__VUE_DEVTOOLS_APP_RECORD_ID__ const instanceId = instance === instance.root ? 'root' : instance.uid return `${appId}:${instanceId}` } -export function getRenderKey (value): string { - if (value == null) return +export function getRenderKey(value): string { + if (value == null) { + return + } const type = typeof value if (type === 'number') { return value - } else if (type === 'string') { + } + else if (type === 'string') { return `'${value}'` - } else if (Array.isArray(value)) { + } + else if (Array.isArray(value)) { return 'Array' - } else { + } + else { return 'Object' } } -export function getComponentInstances (app: App): ComponentInstance[] { +export function getComponentInstances(app: App): ComponentInstance[] { const appRecord = app.__VUE_DEVTOOLS_APP_RECORD__ const appId = appRecord.id.toString() return [...appRecord.instanceMap] .filter(([key]) => key.split(':')[0] === appId) - .map(([,instance]) => instance) // eslint-disable-line comma-spacing + .map(([,instance]) => instance) } diff --git a/packages/app-backend-vue3/src/index.ts b/packages/app-backend-vue3/src/index.ts index cfdf4c004..b86cf5e22 100644 --- a/packages/app-backend-vue3/src/index.ts +++ b/packages/app-backend-vue3/src/index.ts @@ -1,26 +1,27 @@ import { defineBackend } from '@vue-devtools/app-backend-api' +import { HookEvents, backendInjections } from '@vue-devtools/shared-utils' import { ComponentWalker } from './components/tree' -import { editState, getInstanceDetails, getCustomInstanceDetails, getCustomObjectDetails } from './components/data' -import { getInstanceName, getComponentInstances } from './components/util' +import { editState, getCustomInstanceDetails, getCustomObjectDetails, getInstanceDetails } from './components/data' +import { getComponentInstances, getInstanceName } from './components/util' import { getComponentInstanceFromElement, getInstanceOrVnodeRect, getRootElementsFromComponentInstance } from './components/el' -import { backendInjections, HookEvents } from '@vue-devtools/shared-utils' export const backend = defineBackend({ frameworkVersion: 3, features: [], - setup (api) { - api.on.getAppRecordName(payload => { + setup(api) { + api.on.getAppRecordName((payload) => { if (payload.app._component) { payload.name = payload.app._component.name } }) - api.on.getAppRootInstance(payload => { + api.on.getAppRootInstance((payload) => { if (payload.app._instance) { payload.root = payload.app._instance - } else if (payload.app._container?._vnode?.component) { + } + else if (payload.app._container?._vnode?.component) { payload.root = payload.app._container?._vnode?.component } }) @@ -44,23 +45,23 @@ export const backend = defineBackend({ payload.instanceData = getInstanceDetails(payload.componentInstance, ctx) }) - api.on.getComponentName(payload => { + api.on.getComponentName((payload) => { payload.name = getInstanceName(payload.componentInstance) }) - api.on.getComponentBounds(payload => { + api.on.getComponentBounds((payload) => { payload.bounds = getInstanceOrVnodeRect(payload.componentInstance) }) - api.on.getElementComponent(payload => { + api.on.getElementComponent((payload) => { payload.componentInstance = getComponentInstanceFromElement(payload.element) }) - api.on.getComponentInstances(payload => { + api.on.getComponentInstances((payload) => { payload.componentInstances = getComponentInstances(payload.app) }) - api.on.getComponentRootElements(payload => { + api.on.getComponentRootElements((payload) => { payload.rootElements = getRootElementsFromComponentInstance(payload.componentInstance) }) @@ -68,15 +69,15 @@ export const backend = defineBackend({ editState(payload, api.stateEditor, ctx) }) - api.on.getComponentDevtoolsOptions(payload => { + api.on.getComponentDevtoolsOptions((payload) => { payload.options = payload.componentInstance.type.devtools }) - api.on.getComponentRenderCode(payload => { + api.on.getComponentRenderCode((payload) => { payload.code = !(payload.componentInstance.type instanceof Function) ? payload.componentInstance.render.toString() : payload.componentInstance.type.toString() }) - api.on.transformCall(payload => { + api.on.transformCall((payload) => { if (payload.callName === HookEvents.COMPONENT_UPDATED) { const component = payload.inArgs[0] payload.outArgs = [ diff --git a/packages/app-backend-vue3/src/util.ts b/packages/app-backend-vue3/src/util.ts index c03747cfc..ebf171be0 100644 --- a/packages/app-backend-vue3/src/util.ts +++ b/packages/app-backend-vue3/src/util.ts @@ -1,18 +1,21 @@ -import path from 'path' - -export function flatten (items) { +export function flatten(items) { return items.reduce((acc, item) => { - if (item instanceof Array) acc.push(...flatten(item)) - else if (item) acc.push(item) + if (Array.isArray(item)) { + acc.push(...flatten(item)) + } + else if (item) { + acc.push(item) + } return acc }, []) } -export function returnError (cb: () => any) { +export function returnError(cb: () => any) { try { return cb() - } catch (e) { + } + catch (e) { return e } } diff --git a/packages/app-backend-vue3/tsconfig.json b/packages/app-backend-vue3/tsconfig.json index f565bc660..becb763b2 100644 --- a/packages/app-backend-vue3/tsconfig.json +++ b/packages/app-backend-vue3/tsconfig.json @@ -3,25 +3,25 @@ "target": "ES2019", "module": "commonjs", "moduleResolution": "node", - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, "resolveJsonModule": true, - "skipLibCheck": true, "types": [ "node", "webpack-env" ], - "sourceMap": true, - "preserveWatchOutput": true, + "strictBindCallApply": true, + "strictFunctionTypes": true, + "alwaysStrict": true, // Strict "noImplicitAny": false, "noImplicitThis": true, - "alwaysStrict": true, - "strictBindCallApply": true, - "strictFunctionTypes": true, + "sourceMap": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "skipLibCheck": true, + "preserveWatchOutput": true }, "include": [ - "src/**/*", + "src/**/*" ], "exclude": [ "node_modules" diff --git a/packages/app-frontend/src/app.ts b/packages/app-frontend/src/app.ts index 20fe47514..15da8110b 100644 --- a/packages/app-frontend/src/app.ts +++ b/packages/app-frontend/src/app.ts @@ -1,6 +1,7 @@ +import type { App as VueApp } from 'vue' +import { createApp as createVueApp } from 'vue' +import { BridgeEvents, SharedData, destroySharedData, initEnv, initSharedData, isChrome } from '@vue-devtools/shared-utils' import App from './features/App.vue' -import { App as VueApp, createApp as createVueApp } from 'vue' -import { isChrome, initEnv, SharedData, initSharedData, destroySharedData, BridgeEvents } from '@vue-devtools/shared-utils' import { createRouterInstance } from './router' import { getBridge, setBridge } from './features/bridge' import { setAppConnected, setAppInitializing } from './features/connection' @@ -14,7 +15,7 @@ import { setupPlugins } from './plugins' // Capture and log devtool errors when running as actual extension // so that we can debug it by inspecting the background page. // We do want the errors to be thrown in the dev shell though. -export function createApp () { +export function createApp() { const router = createRouterInstance() const app = createVueApp(App) @@ -38,16 +39,17 @@ export function createApp () { * Connect then init the app. We need to reconnect on every reload, because a * new backend will be injected. */ -export function connectApp (app: VueApp, shell) { - shell.connect(async bridge => { +export function connectApp(app: VueApp, shell) { + shell.connect(async (bridge) => { setBridge(bridge) // @TODO remove - // @ts-ignore + // @ts-expect-error custom prop on window window.bridge = bridge if (app.config.globalProperties.$shared) { destroySharedData() - } else { + } + else { Object.defineProperty(app.config.globalProperties, '$shared', { get: () => SharedData, }) diff --git a/packages/app-frontend/src/assets/github-theme/dark.json b/packages/app-frontend/src/assets/github-theme/dark.json index c409cabde..84bf8ad01 100644 --- a/packages/app-frontend/src/assets/github-theme/dark.json +++ b/packages/app-frontend/src/assets/github-theme/dark.json @@ -532,4 +532,4 @@ } ], "encodedTokensColors": [] -} \ No newline at end of file +} diff --git a/packages/app-frontend/src/assets/github-theme/light.json b/packages/app-frontend/src/assets/github-theme/light.json index 4a23a96b9..42da7a39b 100644 --- a/packages/app-frontend/src/assets/github-theme/light.json +++ b/packages/app-frontend/src/assets/github-theme/light.json @@ -528,4 +528,4 @@ } ], "encodedTokensColors": [] -} \ No newline at end of file +} diff --git a/packages/app-frontend/src/features/App.vue b/packages/app-frontend/src/features/App.vue index 6ad19b066..bd7429ae4 100644 --- a/packages/app-frontend/src/features/App.vue +++ b/packages/app-frontend/src/features/App.vue @@ -1,4 +1,14 @@