diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 7f94db3683..26e38ea04d 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,7 +1,5 @@ { "build": { "dockerfile": "Dockerfile" }, - "extensions": [ - "golang.go" - ], - "forwardPorts": [4040] + "extensions": ["golang.go"], + "forwardPorts": [4040] } diff --git a/.eslintrc b/.eslintrc index 71fc9399f4..0f258e3788 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,11 +1,15 @@ { "parser": "babel-eslint", "plugins": ["prettier"], - "extends": ["airbnb", "prettier"], + "extends": [ + "airbnb", + "prettier", + "prettier/react", + "plugin:cypress/recommended" + ], "rules": { "import/no-extraneous-dependencies": ["error", { "devDependencies": true }], "no-unused-vars": ["warn"], - "prettier/prettier": ["error"], "no-param-reassign": ["warn"], "no-shadow": ["warn"], "no-case-declarations": ["warn"], @@ -16,15 +20,17 @@ "jsx-a11y/heading-has-content": ["warn"], "jsx-a11y/control-has-associated-label": ["warn"], "no-undef": ["warn"], - "react/jsx-one-expression-per-line": ["warn"], "jsx-a11y/mouse-events-have-key-events": ["warn"], "jsx-a11y/click-events-have-key-events": ["warn"], "jsx-a11y/no-static-element-interactions": ["warn"], - "jsx-a11y/label-has-associated-control": [ "error", { - "required": { - "some": [ "nesting", "id" ] + "jsx-a11y/label-has-associated-control": [ + "error", + { + "required": { + "some": ["nesting", "id"] + } } - }] + ] }, "env": { "browser": true, diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000000..db8332f4f8 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1 @@ +fca0fcf152cb304c10b94bd3b78d56a319485170 diff --git a/.github/workflows/lint-go.yml b/.github/workflows/lint-go.yml index 35b325f636..cbee7b3496 100644 --- a/.github/workflows/lint-go.yml +++ b/.github/workflows/lint-go.yml @@ -16,4 +16,4 @@ jobs: uses: petethepig/revive-action@v5 with: config: revive.toml - exclude: "vendor/..." + exclude: 'vendor/...' diff --git a/.github/workflows/lint-markdown.yml b/.github/workflows/lint-markdown.yml index fa3a61521c..06ad2001ab 100644 --- a/.github/workflows/lint-markdown.yml +++ b/.github/workflows/lint-markdown.yml @@ -3,14 +3,13 @@ name: Markdown Linting # runs every monday at 9 am on: schedule: - - cron: "0 9 * * 1" + - cron: '0 9 * * 1' jobs: markdown-lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: gaurav-nelson/github-action-markdown-link-check@v1 - with: - config-file: .github/workflows/markdown-links-config.json - + - uses: actions/checkout@v2 + - uses: gaurav-nelson/github-action-markdown-link-check@v1 + with: + config-file: .github/workflows/markdown-links-config.json diff --git a/.github/workflows/tests-js.yml b/.github/workflows/tests-js.yml index 99b4603f98..b8da39f8ca 100644 --- a/.github/workflows/tests-js.yml +++ b/.github/workflows/tests-js.yml @@ -18,3 +18,5 @@ jobs: run: yarn run test - name: Run Webapp lints run: yarn lint + - name: Run Webapp Format check + run: yarn format:check diff --git a/.github/workflows/update-contributos.yml b/.github/workflows/update-contributos.yml index f4976353f7..1d0dcf53f2 100644 --- a/.github/workflows/update-contributos.yml +++ b/.github/workflows/update-contributos.yml @@ -9,17 +9,17 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - with: - ref: ${{ github.head_ref }} + - uses: actions/checkout@v2 + with: + ref: ${{ github.head_ref }} - - name: Install dependencies - run: make install-web-dependencies + - name: Install dependencies + run: make install-web-dependencies - - name: Update contributors - run: make update-contributors + - name: Update contributors + run: make update-contributors - - uses: stefanzweifel/git-auto-commit-action@v4 - with: - commit_message: Updates the list of contributors in README - file_pattern: README.md + - uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: Updates the list of contributors in README + file_pattern: README.md diff --git a/.husky/.gitignore b/.husky/.gitignore new file mode 100644 index 0000000000..31354ec138 --- /dev/null +++ b/.husky/.gitignore @@ -0,0 +1 @@ +_ diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000000..f23377e959 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +yarn run lint-staged diff --git a/.linelint.yml b/.linelint.yml index 5094f7ee44..96df1b7b65 100644 --- a/.linelint.yml +++ b/.linelint.yml @@ -19,7 +19,6 @@ ignore: - examples/dotnet - .github/markdown-images - rules: # checks if file ends in a newline character end-of-file: diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000..e87d8cb17b --- /dev/null +++ b/.prettierignore @@ -0,0 +1,5 @@ +webapp/public +third_party +*.md +monitoring/gen/* +.prettierignore diff --git a/.prettierrc b/.prettierrc index b293633093..637787883f 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1 +1 @@ -{ "single-quote": true } +{ "singleQuote": true } diff --git a/Dockerfile b/Dockerfile index 79a23ad6ed..483ddedb0b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -55,7 +55,7 @@ WORKDIR /opt/pyroscope COPY scripts ./scripts COPY webapp ./webapp -COPY package.json yarn.lock babel.config.js .eslintrc .eslintignore Makefile ./ +COPY package.json yarn.lock babel.config.js .eslintrc .eslintignore .prettierrc Makefile ./ ARG EXTRA_METADATA="" RUN EXTRA_METADATA=$EXTRA_METADATA make assets-release diff --git a/babel.config.js b/babel.config.js index 2199ddd662..2442a78a14 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,4 +1,4 @@ module.exports = { - presets: ["@babel/preset-env", "@babel/preset-react"], - plugins: ["transform-class-properties"], + presets: ['@babel/preset-env', '@babel/preset-react'], + plugins: ['transform-class-properties'], }; diff --git a/benchmark/ci/pr/dangerfile.js b/benchmark/ci/pr/dangerfile.js index 88067d62ba..8700578eda 100644 --- a/benchmark/ci/pr/dangerfile.js +++ b/benchmark/ci/pr/dangerfile.js @@ -1,13 +1,11 @@ -import {markdown } from "danger" +import { markdown } from 'danger'; const fs = require('fs'); const path = require('path'); - -const metaReport = fs.readFileSync(path.join(__dirname,"./meta-report")); -const tableReport = fs.readFileSync(path.join(__dirname,"./table-report")); -const imgReport = fs.readFileSync(path.join(__dirname,"./image-report")); +const metaReport = fs.readFileSync(path.join(__dirname, './meta-report')); +const tableReport = fs.readFileSync(path.join(__dirname, './table-report')); +const imgReport = fs.readFileSync(path.join(__dirname, './image-report')); // the markdown calls seem to be LIFO // so it's easier to just use a single call markdown(`${metaReport} \n${tableReport} \n${imgReport}`); - diff --git a/benchmark/ci/pr/docker-compose.yml b/benchmark/ci/pr/docker-compose.yml index ae241995d7..92825c2c15 100644 --- a/benchmark/ci/pr/docker-compose.yml +++ b/benchmark/ci/pr/docker-compose.yml @@ -2,7 +2,7 @@ x-PYROSCOPE_CPUS: &PYROSCOPE_CPUS 1 x-PYROSCOPE_MEMORY: &PYROSCOPE_MEMORY 4096MB -version: "3.9" +version: '3.9' services: client: build: @@ -10,12 +10,12 @@ services: dockerfile: benchmark/Dockerfile # we will be running docker exec against it command: - - sh - - -c - - sleep Infinity + - sh + - -c + - sleep Infinity volumes: - - ./dashboard-screenshots:/screenshots - - ./report.yaml:/report.yaml + - ./dashboard-screenshots:/screenshots + - ./report.yaml:/report.yaml pyroscope: environment: - PYROSCOPE_LOG_LEVEL=info diff --git a/benchmark/ci/pr/report.yaml b/benchmark/ci/pr/report.yaml index ad2a13bcd0..e019c7cd35 100644 --- a/benchmark/ci/pr/report.yaml +++ b/benchmark/ci/pr/report.yaml @@ -2,7 +2,7 @@ baseName: main targetName: pr queries: - - name: "throughput" + - name: 'throughput' base: 'rate(pyroscope_http_request_duration_seconds_count{handler="/ingest", instance="pyroscope:4040"}[5m])' target: 'rate(pyroscope_http_request_duration_seconds_count{handler="/ingest", instance="pyroscope_main:4040"}[5m])' diffThresholdPercent: 5 @@ -10,6 +10,6 @@ queries: - base: 'pyroscope_http_request_duration_seconds_count{handler="/ingest", instance="pyroscope:4040"}' target: 'pyroscope_http_request_duration_seconds_count{handler="/ingest", instance="pyroscope_main:4040"}' - name: "total items processed" + name: 'total items processed' diffThresholdPercent: 5 biggerIsBetter: true diff --git a/benchmark/docker-compose.yml b/benchmark/docker-compose.yml index 8377df8b1b..ef1625ff61 100644 --- a/benchmark/docker-compose.yml +++ b/benchmark/docker-compose.yml @@ -1,18 +1,18 @@ -version: "3.9" +version: '3.9' services: pyrobench: env_file: - - run-parameters.env + - run-parameters.env build: context: ../ dockerfile: benchmark/Dockerfile # we will be running docker exec against it command: - - sh - - -c - - sleep Infinity + - sh + - -c + - sleep Infinity volumes: - - ./dashboard-screenshots:/screenshots + - ./dashboard-screenshots:/screenshots pyroscope: environment: @@ -28,8 +28,8 @@ services: deploy: resources: limits: - cpus: "${PYROSCOPE_CPUS}" - memory: "${PYROSCOPE_MEMORY}" + cpus: '${PYROSCOPE_CPUS}' + memory: '${PYROSCOPE_MEMORY}' prometheus: image: prom/prometheus:v2.27.1 @@ -53,4 +53,3 @@ services: volumes: data-prometheus: driver: local - diff --git a/benchmark/grafana-provisioning/dashboards/main.json b/benchmark/grafana-provisioning/dashboards/main.json index 22db0d879b..4220f0203b 100644 --- a/benchmark/grafana-provisioning/dashboards/main.json +++ b/benchmark/grafana-provisioning/dashboards/main.json @@ -487,9 +487,7 @@ "links": [], "options": { "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "values": false }, @@ -1639,9 +1637,7 @@ "displayMode": "gradient", "orientation": "vertical", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "values": false }, @@ -1816,17 +1812,7 @@ "2h", "1d" ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" - ] + "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] }, "timezone": "browser", "title": "main", diff --git a/benchmark/grafana-provisioning/dashboards/main.yml b/benchmark/grafana-provisioning/dashboards/main.yml index b61231fa2e..736aa1f750 100644 --- a/benchmark/grafana-provisioning/dashboards/main.yml +++ b/benchmark/grafana-provisioning/dashboards/main.yml @@ -1,8 +1,8 @@ apiVersion: 1 providers: -- name: dashboards - type: file - updateIntervalSeconds: 5 - options: - path: /etc/grafana/provisioning/dashboards + - name: dashboards + type: file + updateIntervalSeconds: 5 + options: + path: /etc/grafana/provisioning/dashboards diff --git a/benchmark/prometheus/prometheus.yml b/benchmark/prometheus/prometheus.yml index 90347a68d6..c9b2c4acc8 100644 --- a/benchmark/prometheus/prometheus.yml +++ b/benchmark/prometheus/prometheus.yml @@ -1,15 +1,15 @@ # my global config global: - scrape_interval: 1s # Set the scrape interval to every 15 seconds. Default is every 1 minute. + scrape_interval: 1s # Set the scrape interval to every 15 seconds. Default is every 1 minute. evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. # scrape_timeout is set to the global default (10s). # Alertmanager configuration alerting: alertmanagers: - - static_configs: - - targets: - # - alertmanager:9093 + - static_configs: + - targets: + # - alertmanager:9093 # Load rules once and periodically evaluate them according to the global 'evaluation_interval'. rule_files: @@ -26,8 +26,8 @@ scrape_configs: # scheme defaults to 'http'. static_configs: - - targets: - - 'localhost:9090' - - 'pyroscope:4040' - - 'pyroscope_main:4040' - - 'pushgateway:9091' + - targets: + - 'localhost:9090' + - 'pyroscope:4040' + - 'pyroscope_main:4040' + - 'pushgateway:9091' diff --git a/benchmark/take-screenshot.js b/benchmark/take-screenshot.js index 20f70435fa..b68f6dee35 100644 --- a/benchmark/take-screenshot.js +++ b/benchmark/take-screenshot.js @@ -1,6 +1,6 @@ -console.log("puppeteer start"); +console.log('puppeteer start'); -const puppeteer = require("puppeteer"); +const puppeteer = require('puppeteer'); function timeout(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); @@ -9,15 +9,16 @@ function timeout(ms) { const args = process.argv.slice(2); const from = args[1]; -console.log('from', from) - -(async () => { +console.log( + 'from', + from +)(async () => { const browser = await puppeteer.launch({}); const page = await browser.newPage(); await page.setViewport({ width: 1600, height: 1280 }); await page.goto( `http://localhost:8080/d/65gjqY3Mk/main?orgId=1&from=${from}`, - { waitUntil: "networkidle2" } + { waitUntil: 'networkidle2' } ); await timeout(2000); await page.screenshot({ @@ -27,4 +28,4 @@ console.log('from', from) browser.close(); })(); -console.log("puppeteer stop"); +console.log('puppeteer stop'); diff --git a/codecov.yml b/codecov.yml index dd5b0f4f5f..21f2f3cf7e 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,2 +1,2 @@ ignore: - - "**/*.pb.go" # auto-generated code + - '**/*.pb.go' # auto-generated code diff --git a/cypress/README.md b/cypress/README.md index 589772d4e1..a1217e9707 100644 --- a/cypress/README.md +++ b/cypress/README.md @@ -2,16 +2,16 @@ [Cypress](https://www.cypress.io/) is a end-to-end testing library, used mainly to simulate real user scenario (ie. clicking around and interacting with). - # Running locally + While running the dev server, run `yarn cy:open` Which will spawn a browser, click on the file that you want to work on. It will be refreshed automatically whenever you update that file. Or run `yarn cy:ci` to run all tests. # Writing tests -* Try to use [testids](https://kentcdodds.com/blog/making-your-ui-tests-resilient-to-change/) to select DOM elements -* Since our application is relatively simple, there's no need for Page Objects yet -* [Don't write small tests with single assertions](https://docs.cypress.io/guides/references/best-practices#Creating-tiny-tests-with-a-single-assertion) -* [Mock HTTP requests](https://docs.cypress.io/guides/guides/network-requests#Stub-Responses) when appropriate +- Try to use [testids](https://kentcdodds.com/blog/making-your-ui-tests-resilient-to-change/) to select DOM elements +- Since our application is relatively simple, there's no need for Page Objects yet +- [Don't write small tests with single assertions](https://docs.cypress.io/guides/references/best-practices#Creating-tiny-tests-with-a-single-assertion) +- [Mock HTTP requests](https://docs.cypress.io/guides/guides/network-requests#Stub-Responses) when appropriate diff --git a/cypress/fixtures/render.json b/cypress/fixtures/render.json index 9116cd58a3..db93aa149c 100644 --- a/cypress/fixtures/render.json +++ b/cypress/fixtures/render.json @@ -1,97 +1,39 @@ { - "flamebearer": { - "names": [ - "function_0", - "function_1", - "function_2", - "function_3", - "function_4", - "function_5", - "function_6" - ], - "levels": [ - [ - 32, - 508, - 500, - 0 - ], - [ - 82, - 50, - 135, - 1 - ], - [ - 32, - 508, - 10, - 2 - ], - [ - 12, - 8, - 100, - 2 - ], - [ - 32, - 32, - 47, - 3 - ], - [ - 320, - 50, - 100, - 3 - ], - [ - 11, - 375, - 320, - 4 - ], - [ - 74, - 298, - 70, - 5 - ], - [ - 77, - 111, - 55, - 6 - ] - ], - "numTicks": 50, - "maxSelf": 58, - "spyName": "gospy", - "sampleRate": 100, - "units": "samples" - }, - "metadata": { - "sampleRate": 100, - "spyName": "gospy", - "units": "samples" - }, - "timeline": { - "startTime": 1631138160, - "samples": [ - 1, - 3, - 1, - 2, - 2, - 4, - 2, - 3, - 1, - 1, - 3, - 4 - ], - "durationDelta": 10 - } + "flamebearer": { + "names": [ + "function_0", + "function_1", + "function_2", + "function_3", + "function_4", + "function_5", + "function_6" + ], + "levels": [ + [32, 508, 500, 0], + [82, 50, 135, 1], + [32, 508, 10, 2], + [12, 8, 100, 2], + [32, 32, 47, 3], + [320, 50, 100, 3], + [11, 375, 320, 4], + [74, 298, 70, 5], + [77, 111, 55, 6] + ], + "numTicks": 50, + "maxSelf": 58, + "spyName": "gospy", + "sampleRate": 100, + "units": "samples" + }, + "metadata": { + "sampleRate": 100, + "spyName": "gospy", + "units": "samples" + }, + "timeline": { + "startTime": 1631138160, + "samples": [1, 3, 1, 2, 2, 4, 2, 3, 1, 1, 3, 4], + "durationDelta": 10 + } } diff --git a/cypress/fixtures/render2.json b/cypress/fixtures/render2.json index 7b15df46f9..f229547711 100644 --- a/cypress/fixtures/render2.json +++ b/cypress/fixtures/render2.json @@ -1,70 +1,32 @@ { - "flamebearer": { - "names": [ - "cpu_function_0", - "cpu_function_1", - "cpu_function_2", - "cpu_function_3" - ], - "levels": [ - [ - 32, - 508, - 100, - 0 - ], - [ - 32, - 508, - 100, - 1 - ], - [ - 32, - 508, - 100, - 2 - ], - [ - 32, - 508, - 100, - 2 - ], - [ - 32, - 508, - 100, - 3 - ] - ], - "numTicks": 50, - "maxSelf": 58, - "spyName": "gospy", - "sampleRate": 100, - "units": "samples" - }, - "metadata": { - "sampleRate": 100, - "spyName": "gospy", - "units": "samples" - }, - "timeline": { - "startTime": 1631138160, - "samples": [ - 1, - 3, - 1, - 2, - 2, - 4, - 2, - 3, - 1, - 1, - 3, - 4 - ], - "durationDelta": 10 - } + "flamebearer": { + "names": [ + "cpu_function_0", + "cpu_function_1", + "cpu_function_2", + "cpu_function_3" + ], + "levels": [ + [32, 508, 100, 0], + [32, 508, 100, 1], + [32, 508, 100, 2], + [32, 508, 100, 2], + [32, 508, 100, 3] + ], + "numTicks": 50, + "maxSelf": 58, + "spyName": "gospy", + "sampleRate": 100, + "units": "samples" + }, + "metadata": { + "sampleRate": 100, + "spyName": "gospy", + "units": "samples" + }, + "timeline": { + "startTime": 1631138160, + "samples": [1, 3, 1, 2, 2, 4, 2, 3, 1, 1, 3, 4], + "durationDelta": 10 + } } diff --git a/cypress/fixtures/simple-golang-app-cpu.json b/cypress/fixtures/simple-golang-app-cpu.json index d990b248df..9fc275c2dd 100644 --- a/cypress/fixtures/simple-golang-app-cpu.json +++ b/cypress/fixtures/simple-golang-app-cpu.json @@ -9,46 +9,10 @@ "main.fastFunction" ], "levels": [ - [ - 0, - 988, - 0, - 0 - ], - [ - 0, - 988, - 0, - 1 - ], - [ - 0, - 214, - 0, - 5, - 0, - 3, - 2, - 4, - 0, - 771, - 0, - 2 - ], - [ - 0, - 214, - 214, - 3, - 2, - 1, - 1, - 5, - 0, - 771, - 771, - 3 - ] + [0, 988, 0, 0], + [0, 988, 0, 1], + [0, 214, 0, 5, 0, 3, 2, 4, 0, 771, 0, 2], + [0, 214, 214, 3, 2, 1, 1, 5, 0, 771, 771, 3] ], "numTicks": 988, "maxSelf": 771, @@ -65,10 +29,7 @@ }, "timeline": { "startTime": 1632335270, - "samples": [ - 989 - ], + "samples": [989], "durationDelta": 10 } } - diff --git a/cypress/integration/basic.ts b/cypress/integration/basic.ts index 93dcc64564..b566b98c4c 100644 --- a/cypress/integration/basic.ts +++ b/cypress/integration/basic.ts @@ -1,13 +1,13 @@ -import { BAR_HEIGHT } from '../../webapp/javascript/components/FlameGraph/FlameGraphComponent/index.jsx' +import { BAR_HEIGHT } from '../../webapp/javascript/components/FlameGraph/FlameGraphComponent'; /// describe('basic test', () => { it('successfully loads', () => { - cy.visit('/') + cy.visit('/'); cy.title().should('eq', 'Pyroscope'); }); it('internal sidebar links work', () => { - cy.visit('/') + cy.visit('/'); cy.findByTestId('sidebar-comparison').click(); cy.location('pathname').should('eq', '/comparison'); @@ -19,44 +19,57 @@ describe('basic test', () => { cy.location('pathname').should('eq', '/'); }); - it('app selector works', () => { - cy.intercept('**/render*', { fixture: 'render.json', - times: 1 - }).as('render1') + times: 1, + }).as('render1'); cy.visit('/'); cy.wait('@render1'); cy.fixture('render.json').then((data) => { - cy.findByTestId('table-view').contains('td', data.flamebearer.names[0]).should('be.visible'); - cy.findByTestId('table-view').contains('td', data.flamebearer.names[data.flamebearer.names.length - 1]).should('be.visible'); + cy.findByTestId('table-view') + .contains('td', data.flamebearer.names[0]) + .should('be.visible'); + cy.findByTestId('table-view') + .contains( + 'td', + data.flamebearer.names[data.flamebearer.names.length - 1] + ) + .should('be.visible'); }); cy.intercept('**/render*', { fixture: 'render2.json', - times: 1 - }).as('render2') - + times: 1, + }).as('render2'); + cy.findByTestId('app-name-selector').select('pyroscope.server.cpu'); - + cy.wait('@render2'); cy.fixture('render2.json').then((data) => { - cy.findByTestId('table-view').contains('td', data.flamebearer.names[0]).should('be.visible'); - cy.findByTestId('table-view').contains('td', data.flamebearer.names[data.flamebearer.names.length - 1]).should('be.visible'); + cy.findByTestId('table-view') + .contains('td', data.flamebearer.names[0]) + .should('be.visible'); + cy.findByTestId('table-view') + .contains( + 'td', + data.flamebearer.names[data.flamebearer.names.length - 1] + ) + .should('be.visible'); }); - }); it('updates flamegraph on app name change', () => { - cy.visit('/') + cy.visit('/'); cy.findByTestId('app-name-selector').select('pyroscope.server.cpu'); - cy.findByTestId('flamegraph-canvas').invoke('attr', 'data-appname').should('eq', 'pyroscope.server.cpu{}'); + cy.findByTestId('flamegraph-canvas') + .invoke('attr', 'data-appname') + .should('eq', 'pyroscope.server.cpu{}'); }); it('view buttons should change view when clicked', () => { @@ -64,57 +77,61 @@ describe('basic test', () => { // could have no data cy.intercept('**/render*', { fixture: 'render.json', - times: 1 - }).as('render1') + times: 1, + }).as('render1'); - cy.visit('/') + cy.visit('/'); cy.findByTestId('btn-table-view').click(); cy.findByTestId('table-view').should('be.visible'); cy.findByTestId('flamegraph-view').should('not.exist'); - + cy.findByTestId('btn-both-view').click(); cy.findByTestId('table-view').should('be.visible'); cy.findByTestId('flamegraph-view').should('be.visible'); - + cy.findByTestId('btn-flamegraph-view').click(); cy.findByTestId('table-view').should('not.be.visible'); cy.findByTestId('flamegraph-view').should('be.visible'); }); - it('sorting is working', () => { - /** - * @param row 'first' | 'last' + it('sorting is working', () => { + /** + * @param row 'first' | 'last' * @param column 'location' | 'self' | 'total' - */ + */ const columns = { location: { index: 1, - selector: '.symbol-name' + selector: '.symbol-name', }, self: { index: 2, - selector: 'span' + selector: 'span', }, total: { index: 3, - selector: 'span' - } - } - - const sortColumn = (columnIndex) => { - return cy.findByTestId('table-view').find(`thead > tr > :nth-child(${columnIndex})`).click(); - } - - const getCellContent = (row, column) => { - let query = `tbody > :nth-child(${row}) > :nth-child(${column.index}) > ${column.selector}`; - return cy.findByTestId('table-view').find(query) - .then(cell => cell[0].innerText); - } + selector: 'span', + }, + }; + + const sortColumn = (columnIndex) => + cy + .findByTestId('table-view') + .find(`thead > tr > :nth-child(${columnIndex})`) + .click(); + + const getCellContent = (row, column) => { + const query = `tbody > :nth-child(${row}) > :nth-child(${column.index}) > ${column.selector}`; + return cy + .findByTestId('table-view') + .find(query) + .then((cell) => cell[0].innerText); + }; cy.intercept('**/render*', { fixture: 'render.json', - times: 1 + times: 1, }).as('render'); cy.visit('/'); @@ -125,51 +142,49 @@ describe('basic test', () => { const first = 1; const last = rows.length; - //sort by location desc - sortColumn(columns['location'].index); - getCellContent(first, columns['location']).should('eq', 'function_6'); - getCellContent(last, columns['location']).should('eq', 'function_0'); - - //sort by location asc - sortColumn(columns['location'].index); - getCellContent(first, columns['location']).should('eq', 'function_0'); - getCellContent(last, columns['location']).should('eq', 'function_6'); - - //sort by self desc - sortColumn(columns['self'].index); - getCellContent(first, columns['self']).should('eq', '5.00 seconds'); - getCellContent(last, columns['self']).should('eq', '0.55 seconds'); - - //sort by self asc - sortColumn(columns['self'].index); - getCellContent(first, columns['self']).should('eq', '0.55 seconds'); - getCellContent(last, columns['self']).should('eq', '5.00 seconds'); - - //sort by total desc - sortColumn(columns['total'].index); - getCellContent(first, columns['total']).should('eq', '5.16 seconds'); - getCellContent(last, columns['total']).should('eq', '0.50 seconds'); - - //sort by total asc - sortColumn(columns['total'].index); - getCellContent(first, columns['total']).should('eq', '0.50 seconds'); - getCellContent(last, columns['total']).should('eq', '5.16 seconds'); - }) + // sort by location desc + sortColumn(columns.location.index); + getCellContent(first, columns.location).should('eq', 'function_6'); + getCellContent(last, columns.location).should('eq', 'function_0'); + + // sort by location asc + sortColumn(columns.location.index); + getCellContent(first, columns.location).should('eq', 'function_0'); + getCellContent(last, columns.location).should('eq', 'function_6'); + + // sort by self desc + sortColumn(columns.self.index); + getCellContent(first, columns.self).should('eq', '5.00 seconds'); + getCellContent(last, columns.self).should('eq', '0.55 seconds'); + + // sort by self asc + sortColumn(columns.self.index); + getCellContent(first, columns.self).should('eq', '0.55 seconds'); + getCellContent(last, columns.self).should('eq', '5.00 seconds'); + + // sort by total desc + sortColumn(columns.total.index); + getCellContent(first, columns.total).should('eq', '5.16 seconds'); + getCellContent(last, columns.total).should('eq', '0.50 seconds'); + + // sort by total asc + sortColumn(columns.total.index); + getCellContent(first, columns.total).should('eq', '0.50 seconds'); + getCellContent(last, columns.total).should('eq', '5.16 seconds'); + }); }); it('validates "Reset View" works', () => { cy.intercept('**/render*', { fixture: 'simple-golang-app-cpu.json', - }).as('render') + }).as('render'); - cy.visit('/') + cy.visit('/'); cy.findByTestId('reset-view').should('not.be.visible'); - cy.findByTestId('flamegraph-canvas') - .click(0, BAR_HEIGHT) + cy.findByTestId('flamegraph-canvas').click(0, BAR_HEIGHT); cy.findByTestId('reset-view').should('be.visible'); cy.findByTestId('reset-view').click(); cy.findByTestId('reset-view').should('not.be.visible'); }); -}) - +}); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index f03d7c4c4c..112cf63f17 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -23,4 +23,4 @@ // // -- This will overwrite an existing command -- // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) -import "@testing-library/cypress/add-commands"; +import '@testing-library/cypress/add-commands'; diff --git a/cypress/support/index.js b/cypress/support/index.js index d076cec9fd..37a498fb5b 100644 --- a/cypress/support/index.js +++ b/cypress/support/index.js @@ -14,7 +14,7 @@ // *********************************************************** // Import commands.js using ES2015 syntax: -import "./commands"; +import './commands'; // Alternatively you can use CommonJS syntax: // require('./commands') diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json index 2111baca51..a0acf22b00 100644 --- a/cypress/tsconfig.json +++ b/cypress/tsconfig.json @@ -6,4 +6,3 @@ }, "include": ["**/*.ts"] } - diff --git a/examples/dotnet/fast-slow/docker-compose.yml b/examples/dotnet/fast-slow/docker-compose.yml index 34a71db77e..31da9e0904 100644 --- a/examples/dotnet/fast-slow/docker-compose.yml +++ b/examples/dotnet/fast-slow/docker-compose.yml @@ -1,13 +1,13 @@ --- -version: "3.9" +version: '3.9' services: pyroscope: - image: "pyroscope/pyroscope:latest" + image: 'pyroscope/pyroscope:latest' ports: - - "4040:4040" + - '4040:4040' command: - - "server" + - 'server' app: - build: "" + build: '' cap_add: - SYS_PTRACE diff --git a/examples/dotnet/web/docker-compose.yml b/examples/dotnet/web/docker-compose.yml index 6baaddeffb..57a52fc295 100644 --- a/examples/dotnet/web/docker-compose.yml +++ b/examples/dotnet/web/docker-compose.yml @@ -1,17 +1,17 @@ --- -version: "3.9" +version: '3.9' services: pyroscope: - image: "pyroscope/pyroscope:latest" + image: 'pyroscope/pyroscope:latest' ports: - - "4040:4040" + - '4040:4040' command: - - "server" + - 'server' app: environment: ASPNETCORE_URLS: http://*:5000 ports: - - "5000:5000" - build: "" + - '5000:5000' + build: '' cap_add: - SYS_PTRACE diff --git a/examples/golang/docker-compose.yml b/examples/golang/docker-compose.yml index 5f23f2cf41..c87a4a04d2 100644 --- a/examples/golang/docker-compose.yml +++ b/examples/golang/docker-compose.yml @@ -1,11 +1,11 @@ --- -version: "3.9" +version: '3.9' services: pyroscope: - image: "pyroscope/pyroscope:latest" + image: 'pyroscope/pyroscope:latest' ports: - - "4040:4040" + - '4040:4040' command: - - "server" + - 'server' app: build: . diff --git a/examples/grafana-integration/docker-compose.yml b/examples/grafana-integration/docker-compose.yml index 27251ccf9c..07c8bd65c2 100644 --- a/examples/grafana-integration/docker-compose.yml +++ b/examples/grafana-integration/docker-compose.yml @@ -1,4 +1,4 @@ -version: "3.9" +version: '3.9' services: prometheus: image: prom/prometheus:latest @@ -14,12 +14,12 @@ services: - ./grafana/grafana.ini:/etc/grafana/grafana.ini - ./grafana/home.json:/default-dashboard.json environment: - - "GF_INSTALL_PLUGINS=pyroscope-datasource,pyroscope-panel" + - 'GF_INSTALL_PLUGINS=pyroscope-datasource,pyroscope-panel' ports: - 3000:3000 pyroscope: - image: "pyroscope/pyroscope:latest" + image: 'pyroscope/pyroscope:latest' ports: - 4040:4040 command: @@ -28,7 +28,6 @@ services: - PYROSCOPE_LOG_LEVEL=info - PYROSCOPE_WAIT_AFTER_STOP=true - go-agent: build: context: ../golang diff --git a/examples/grafana-integration/grafana-provisioning/dashboards/main.yml b/examples/grafana-integration/grafana-provisioning/dashboards/main.yml index b61231fa2e..736aa1f750 100644 --- a/examples/grafana-integration/grafana-provisioning/dashboards/main.yml +++ b/examples/grafana-integration/grafana-provisioning/dashboards/main.yml @@ -1,8 +1,8 @@ apiVersion: 1 providers: -- name: dashboards - type: file - updateIntervalSeconds: 5 - options: - path: /etc/grafana/provisioning/dashboards + - name: dashboards + type: file + updateIntervalSeconds: 5 + options: + path: /etc/grafana/provisioning/dashboards diff --git a/examples/grafana-integration/grafana-provisioning/dashboards/server-prometheus.json b/examples/grafana-integration/grafana-provisioning/dashboards/server-prometheus.json index 73e9fc6dbe..6cc141ad47 100644 --- a/examples/grafana-integration/grafana-provisioning/dashboards/server-prometheus.json +++ b/examples/grafana-integration/grafana-provisioning/dashboards/server-prometheus.json @@ -1450,17 +1450,7 @@ "2h", "1d" ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" - ] + "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] }, "timezone": "browser", "title": "Pyroscope Server (Prometheus)", diff --git a/examples/grafana-integration/prometheus/prometheus.yml b/examples/grafana-integration/prometheus/prometheus.yml index d5e432b928..6d817a4287 100644 --- a/examples/grafana-integration/prometheus/prometheus.yml +++ b/examples/grafana-integration/prometheus/prometheus.yml @@ -1,15 +1,15 @@ # my global config global: - scrape_interval: 1s # Set the scrape interval to every 15 seconds. Default is every 1 minute. + scrape_interval: 1s # Set the scrape interval to every 15 seconds. Default is every 1 minute. evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. # scrape_timeout is set to the global default (10s). # Alertmanager configuration alerting: alertmanagers: - - static_configs: - - targets: - # - alertmanager:9093 + - static_configs: + - targets: + # - alertmanager:9093 # Load rules once and periodically evaluate them according to the global 'evaluation_interval'. rule_files: @@ -26,6 +26,6 @@ scrape_configs: # scheme defaults to 'http'. static_configs: - - targets: - - 'localhost:9090' - - 'pyroscope:4040' + - targets: + - 'localhost:9090' + - 'pyroscope:4040' diff --git a/examples/java/docker-compose.yml b/examples/java/docker-compose.yml index a5d8652239..ef5802c3b9 100644 --- a/examples/java/docker-compose.yml +++ b/examples/java/docker-compose.yml @@ -1,13 +1,13 @@ --- -version: "3.9" +version: '3.9' services: pyroscope: - image: "pyroscope/pyroscope:latest" + image: 'pyroscope/pyroscope:latest' environment: - - "PYROSCOPE_LOG_LEVEL=debug" + - 'PYROSCOPE_LOG_LEVEL=debug' ports: - - "4040:4040" + - '4040:4040' command: - - "server" + - 'server' app: build: . diff --git a/examples/php/docker-compose.yml b/examples/php/docker-compose.yml index cb93171b88..451edd9572 100644 --- a/examples/php/docker-compose.yml +++ b/examples/php/docker-compose.yml @@ -1,14 +1,13 @@ --- -version: "3.9" +version: '3.9' services: pyroscope: - image: "pyroscope/pyroscope:latest" + image: 'pyroscope/pyroscope:latest' ports: - - "4040:4040" + - '4040:4040' command: - - "server" + - 'server' app: build: . cap_add: - SYS_PTRACE - diff --git a/examples/python/docker-compose.yml b/examples/python/docker-compose.yml index 5f23f2cf41..2fa7a1f6ab 100644 --- a/examples/python/docker-compose.yml +++ b/examples/python/docker-compose.yml @@ -1,11 +1,14 @@ --- -version: "3.9" +version: '3.9' + services: pyroscope: - image: "pyroscope/pyroscope:latest" + image: 'pyroscope/pyroscope:latest' ports: - - "4040:4040" + - '4040:4040' command: - - "server" + - 'server' app: build: . + cap_add: + - SYS_PTRACE diff --git a/examples/ruby/docker-compose.yml b/examples/ruby/docker-compose.yml index 4c3761b412..451edd9572 100644 --- a/examples/ruby/docker-compose.yml +++ b/examples/ruby/docker-compose.yml @@ -1,12 +1,12 @@ --- -version: "3.9" +version: '3.9' services: pyroscope: - image: "pyroscope/pyroscope:latest" + image: 'pyroscope/pyroscope:latest' ports: - - "4040:4040" + - '4040:4040' command: - - "server" + - 'server' app: build: . cap_add: diff --git a/hacks/metrics-comparison/docker-compose.yml b/hacks/metrics-comparison/docker-compose.yml index f0d1e911ac..44c338486f 100644 --- a/hacks/metrics-comparison/docker-compose.yml +++ b/hacks/metrics-comparison/docker-compose.yml @@ -1,4 +1,4 @@ -version: "3.9" +version: '3.9' services: pyroscope_dev: environment: @@ -15,7 +15,6 @@ services: cpus: 1 memory: 2048MB - pyroscope_reference: environment: - PYROSCOPE_LOG_LEVEL=debug @@ -24,7 +23,7 @@ services: command: - server ports: - - 4041:4040 + - 4041:4040 deploy: resources: limits: @@ -37,14 +36,14 @@ services: - ./grafana-provisioning:/etc/grafana/provisioning - ./grafana/grafana.ini:/etc/grafana/grafana.ini environment: - - "GF_INSTALL_PLUGINS=pyroscope-datasource,pyroscope-panel" + - 'GF_INSTALL_PLUGINS=pyroscope-datasource,pyroscope-panel' ports: - - 3000:3000 + - 3000:3000 prometheus: image: prom/prometheus:latest command: - - "--config.file=/etc/prometheus/prometheus.yml" + - '--config.file=/etc/prometheus/prometheus.yml' volumes: - ./prometheus:/etc/prometheus/ - data-prometheus:/prometheus @@ -53,13 +52,13 @@ services: build: context: ./golang environment: - - "PYROSCOPE_URL=pyroscope_dev:4040" + - 'PYROSCOPE_URL=pyroscope_dev:4040' go-agent-ref: build: context: ./golang environment: - - "PYROSCOPE_URL=pyroscope_reference:4040" + - 'PYROSCOPE_URL=pyroscope_reference:4040' volumes: data-prometheus: diff --git a/hacks/metrics-comparison/grafana-provisioning/dashboards/main.yml b/hacks/metrics-comparison/grafana-provisioning/dashboards/main.yml index b61231fa2e..736aa1f750 100644 --- a/hacks/metrics-comparison/grafana-provisioning/dashboards/main.yml +++ b/hacks/metrics-comparison/grafana-provisioning/dashboards/main.yml @@ -1,8 +1,8 @@ apiVersion: 1 providers: -- name: dashboards - type: file - updateIntervalSeconds: 5 - options: - path: /etc/grafana/provisioning/dashboards + - name: dashboards + type: file + updateIntervalSeconds: 5 + options: + path: /etc/grafana/provisioning/dashboards diff --git a/hacks/metrics-comparison/prometheus/prometheus.yml b/hacks/metrics-comparison/prometheus/prometheus.yml index 01f3322fdd..02347670f4 100644 --- a/hacks/metrics-comparison/prometheus/prometheus.yml +++ b/hacks/metrics-comparison/prometheus/prometheus.yml @@ -1,15 +1,15 @@ # my global config global: - scrape_interval: 1s # Set the scrape interval to every 15 seconds. Default is every 1 minute. + scrape_interval: 1s # Set the scrape interval to every 15 seconds. Default is every 1 minute. evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. # scrape_timeout is set to the global default (10s). # Alertmanager configuration alerting: alertmanagers: - - static_configs: - - targets: - # - alertmanager:9093 + - static_configs: + - targets: + # - alertmanager:9093 # Load rules once and periodically evaluate them according to the global 'evaluation_interval'. rule_files: @@ -26,7 +26,7 @@ scrape_configs: # scheme defaults to 'http'. static_configs: - - targets: - # - 'localhost:9090' - - 'pyroscope_dev:4040' - - 'pyroscope_reference:4040' + - targets: + # - 'localhost:9090' + - 'pyroscope_dev:4040' + - 'pyroscope_reference:4040' diff --git a/package.json b/package.json index b1f8ea9b39..fe336b1493 100644 --- a/package.json +++ b/package.json @@ -9,12 +9,17 @@ "url": "https://github.com/pyroscope-io/pyroscope.git" }, "scripts": { + "postinstall": "./scripts/web-postinstall.sh", "dev": "webpack --progress --config scripts/webpack/webpack.dev.js", "test": "jest", - "lint": "eslint ./ --ext .js,.jsx --cache --fix", + "lint": "eslint ./ --ext .js,.jsx,.ts,.tsx --cache --fix", + "format": "prettier --write .", + "format:check": "prettier --check .", "size-build": "make assets-size-build", "cy:open": "cypress open", - "cy:ci": "cypress run" + "cy:ci": "cypress run", + "prepare": "husky install", + "lint-staged": "lint-staged" }, "devDependencies": { "@babel/core": "7.8.4", @@ -36,6 +41,7 @@ "eslint": "7.2.0", "eslint-config-airbnb": "18.2.1", "eslint-config-prettier": "^7.1.0", + "eslint-plugin-cypress": "^2.12.1", "eslint-plugin-import": "^2.22.1", "eslint-plugin-jsx-a11y": "^6.4.1", "eslint-plugin-prettier": "^3.3.1", @@ -43,7 +49,9 @@ "eslint-plugin-react-hooks": "4.0.0", "eslint-webpack-plugin": "^2.4.1", "html-webpack-plugin": "^5.3.2", + "husky": "^7.0.2", "jest": "^26.6.3", + "lint-staged": "^11.1.2", "mini-css-extract-plugin": "^2.2.0", "monaco-editor-webpack-plugin": "^1.9.0", "postcss-browser-reporter": "^0.6.0", @@ -111,5 +119,11 @@ "path": "webapp/public/assets/app.js", "limit": "750 kB" } - ] + ], + "lint-staged": { + "*.{js,jsx,ts,tsx}": [ + "eslint --cache --fix" + ], + "*.{js,jsx,ts,tsx,json,yml,yaml,eslintrc,prettierrc}": "prettier --write" + } } diff --git a/pkg/cli/testdata/agent.yml b/pkg/cli/testdata/agent.yml index b89c4c8f87..44f3a793b4 100644 --- a/pkg/cli/testdata/agent.yml +++ b/pkg/cli/testdata/agent.yml @@ -2,10 +2,10 @@ log-level: error targets: - - service-name: foo - application-name: foo.app - spy-name: debugspy + - service-name: foo + application-name: foo.app + spy-name: debugspy tags: - foo: bar - baz: qux + foo: bar + baz: qux diff --git a/pkg/cli/testdata/example.yml b/pkg/cli/testdata/example.yml index 71cd05621a..d1003c2674 100644 --- a/pkg/cli/testdata/example.yml +++ b/pkg/cli/testdata/example.yml @@ -1,11 +1,11 @@ --- -foo: "test-val-1" +foo: 'test-val-1' foos: - - "test-val-2" - - "test-val-3" + - 'test-val-2' + - 'test-val-3' bar: 123 -baz: "10h" -foo-bar: "test-val-4" +baz: '10h' +foo-bar: 'test-val-4' foo-foo: 10.23 -foo-bytes: "100mb" -foo-dur: "5m23s" +foo-bytes: '100mb' +foo-dur: '5m23s' diff --git a/scripts/web-postinstall.sh b/scripts/web-postinstall.sh new file mode 100755 index 0000000000..27d9ade90f --- /dev/null +++ b/scripts/web-postinstall.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# web-postinstall.sh +# to be run by yarn/npm after installing + +# don't run where git is not present (probably CI) +if command -v git; then + # makes git blame ignore commits that are purely reformatting code + git config blame.ignoreRevsFile .git-blame-ignore-revs +fi diff --git a/scripts/webpack/postcss.config.js b/scripts/webpack/postcss.config.js index 1b5204fab9..6055e05eb1 100644 --- a/scripts/webpack/postcss.config.js +++ b/scripts/webpack/postcss.config.js @@ -1,7 +1,7 @@ module.exports = () => ({ plugins: { autoprefixer: {}, - "postcss-reporter": {}, - "postcss-browser-reporter": {}, + 'postcss-reporter': {}, + 'postcss-browser-reporter': {}, }, }); diff --git a/scripts/webpack/webpack.common.js b/scripts/webpack/webpack.common.js index af98517788..f85f16ea5a 100644 --- a/scripts/webpack/webpack.common.js +++ b/scripts/webpack/webpack.common.js @@ -1,15 +1,15 @@ -const webpack = require("webpack"); -const path = require("path"); -const glob = require("glob"); -const HtmlWebpackPlugin = require("html-webpack-plugin"); -const MiniCssExtractPlugin = require("mini-css-extract-plugin"); -const CopyPlugin = require("copy-webpack-plugin"); -const ESLintPlugin = require("eslint-webpack-plugin"); +const webpack = require('webpack'); +const path = require('path'); +const glob = require('glob'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +const CopyPlugin = require('copy-webpack-plugin'); +const ESLintPlugin = require('eslint-webpack-plugin'); -const fs = require("fs"); +const fs = require('fs'); const pages = glob - .sync("./webapp/templates/*.html") + .sync('./webapp/templates/*.html') .map((x) => path.basename(x)); const pagePlugins = pages.map( (name) => @@ -17,11 +17,11 @@ const pagePlugins = pages.map( filename: path.resolve(__dirname, `../../webapp/public/${name}`), template: path.resolve(__dirname, `../../webapp/templates/${name}`), inject: false, - chunksSortMode: "none", + chunksSortMode: 'none', templateParameters: (compilation, assets, options) => ({ extra_metadata: process.env.EXTRA_METADATA ? fs.readFileSync(process.env.EXTRA_METADATA) - : "", + : '', mode: process.env.NODE_ENV, webpack: compilation.getStats().toJson(), compilation, @@ -35,31 +35,31 @@ const pagePlugins = pages.map( ); module.exports = { - target: "web", + target: 'web', entry: { - app: "./webapp/javascript/index.jsx", - styles: "./webapp/sass/profile.scss", + app: './webapp/javascript/index.jsx', + styles: './webapp/sass/profile.scss', }, output: { - publicPath: "", - path: path.resolve(__dirname, "../../webapp/public/assets"), - filename: "[name].[hash].js", + publicPath: '', + path: path.resolve(__dirname, '../../webapp/public/assets'), + filename: '[name].[hash].js', clean: true, }, resolve: { - extensions: [".ts", ".tsx", ".es6", ".js", ".jsx", ".json", ".svg"], + extensions: ['.ts', '.tsx', '.es6', '.js', '.jsx', '.json', '.svg'], alias: { // rc-trigger uses babel-runtime which has internal dependency to core-js@2 // this alias maps that dependency to core-js@t3 - "core-js/library/fn": "core-js/stable", + 'core-js/library/fn': 'core-js/stable', }, modules: [ - "node_modules", - path.resolve("webapp"), - path.resolve("node_modules"), + 'node_modules', + path.resolve('webapp'), + path.resolve('node_modules'), ], }, @@ -80,30 +80,30 @@ module.exports = { test: /\.jsx?$/, use: [ { - loader: "babel-loader", + loader: 'babel-loader', options: { cacheDirectory: true, babelrc: true, // Note: order is bottom-to-top and/or right-to-left presets: [ [ - "@babel/preset-env", + '@babel/preset-env', { targets: { - browsers: "last 3 versions", + browsers: 'last 3 versions', }, - useBuiltIns: "entry", + useBuiltIns: 'entry', corejs: 3, modules: false, }, ], [ - "@babel/preset-typescript", + '@babel/preset-typescript', { allowNamespaces: true, }, ], - "@babel/preset-react", + '@babel/preset-react', ], }, }, @@ -113,9 +113,9 @@ module.exports = { test: /\.js$/, use: [ { - loader: "babel-loader", + loader: 'babel-loader', options: { - presets: [["@babel/preset-env"]], + presets: [['@babel/preset-env']], }, }, ], @@ -123,14 +123,14 @@ module.exports = { { test: /\.css$/, // include: MONACO_DIR, // https://github.com/react-monaco-editor/react-monaco-editor - use: ["style-loader", "css-loader"], + use: ['style-loader', 'css-loader'], }, { test: /\.scss$/, use: [ MiniCssExtractPlugin.loader, { - loader: "css-loader", + loader: 'css-loader', options: { importLoaders: 2, url: true, @@ -138,14 +138,14 @@ module.exports = { }, }, { - loader: "postcss-loader", + loader: 'postcss-loader', options: { sourceMap: true, config: { path: __dirname }, }, }, { - loader: "sass-loader", + loader: 'sass-loader', options: { sourceMap: true, }, @@ -154,8 +154,8 @@ module.exports = { }, { test: /\.(svg|ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/, - loader: "file-loader", - options: { name: "static/img/[name].[hash:8].[ext]" }, + loader: 'file-loader', + options: { name: 'static/img/[name].[hash:8].[ext]' }, }, ], }, @@ -163,19 +163,19 @@ module.exports = { plugins: [ new ESLintPlugin(), new webpack.ProvidePlugin({ - $: "jquery", - jQuery: "jquery", + $: 'jquery', + jQuery: 'jquery', }), ...pagePlugins, new MiniCssExtractPlugin({ - filename: "[name].[hash].css", + filename: '[name].[hash].css', }), new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), new CopyPlugin({ patterns: [ { - from: "webapp/images", - to: "images", + from: 'webapp/images', + to: 'images', }, ], }), diff --git a/scripts/webpack/webpack.dev.js b/scripts/webpack/webpack.dev.js index 3b6885ee12..bfc37f3a05 100644 --- a/scripts/webpack/webpack.dev.js +++ b/scripts/webpack/webpack.dev.js @@ -1,9 +1,9 @@ -const { merge } = require("webpack-merge"); -const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); +const { merge } = require('webpack-merge'); +const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); -const common = require("./webpack.common.js"); +const common = require('./webpack.common.js'); module.exports = merge(common, { - mode: "development", + mode: 'development', plugins: [new BundleAnalyzerPlugin()], }); diff --git a/scripts/webpack/webpack.panel.js b/scripts/webpack/webpack.panel.js index 9bf41cbd50..0dd5080920 100644 --- a/scripts/webpack/webpack.panel.js +++ b/scripts/webpack/webpack.panel.js @@ -1,22 +1,22 @@ -const { merge } = require("webpack-merge"); +const { merge } = require('webpack-merge'); -const path = require("path"); -const prod = require("./webpack.prod.js"); +const path = require('path'); +const prod = require('./webpack.prod.js'); module.exports = merge(prod, { entry: { flamegraphComponent: - "./webapp/javascript/components/FlameGraph/FlameGraphComponent/index.jsx", + './webapp/javascript/components/FlameGraph/FlameGraphComponent/index.jsx', }, output: { - publicPath: "", - path: path.resolve(__dirname, "../../webapp/public/assets"), - filename: "[name].js", + publicPath: '', + path: path.resolve(__dirname, '../../webapp/public/assets'), + filename: '[name].js', clean: true, - library: "pyroscope", - libraryTarget: "umd", + library: 'pyroscope', + libraryTarget: 'umd', umdNamedDefine: true, }, }); diff --git a/scripts/webpack/webpack.prod.js b/scripts/webpack/webpack.prod.js index 45fa999511..39fc00e643 100644 --- a/scripts/webpack/webpack.prod.js +++ b/scripts/webpack/webpack.prod.js @@ -1,7 +1,7 @@ -const { merge } = require("webpack-merge"); +const { merge } = require('webpack-merge'); -const common = require("./webpack.common.js"); +const common = require('./webpack.common.js'); module.exports = merge(common, { - mode: "production", + mode: 'production', }); diff --git a/webapp/__tests__/DateRangePicker.spec.js b/webapp/__tests__/DateRangePicker.spec.js index 4552851fc3..8b84aef6c7 100644 --- a/webapp/__tests__/DateRangePicker.spec.js +++ b/webapp/__tests__/DateRangePicker.spec.js @@ -1,35 +1,32 @@ -import React from "react"; -import renderer from "react-test-renderer"; +import React from 'react'; +import renderer from 'react-test-renderer'; -import { configure, mount , shallow,render} from "enzyme"; -import Adapter from "enzyme-adapter-react-16"; +import { configure, mount, shallow, render } from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; -import DateRangePicker from "../javascript/components/DateRangePicker.jsx"; +import DateRangePicker from '../javascript/components/DateRangePicker.jsx'; -jest.mock("react-redux", () => ({ - connect: () => jest.fn(), - useSelector: () => ({ - from: 'state', - until: 'state', - }), - useDispatch: () => jest.fn() - })); +jest.mock('react-redux', () => ({ + connect: () => jest.fn(), + useSelector: () => ({ + from: 'state', + until: 'state', + }), + useDispatch: () => jest.fn(), +})); configure({ adapter: new Adapter() }); -describe("DateRangePicker", () => { - it("When date changes, flamegraph component should update", () => { - const wrapper = shallow( - - ); - expect(wrapper.find("button").length).toEqual(17); - - expect(wrapper.find("button").at(1).text()).toBe('Last 5 minutes'); - expect(wrapper.find("button").at(2).text()).toBe('Last 15 minutes'); - expect(wrapper.find("button").at(3).text()).toBe('Last 30 minutes'); - expect(wrapper.find("button").at(4).text()).toBe('Last 1 hour'); +describe('DateRangePicker', () => { + it('When date changes, flamegraph component should update', () => { + const wrapper = shallow(); + expect(wrapper.find('button').length).toEqual(17); - wrapper.find("button").at(1).simulate("click"); + expect(wrapper.find('button').at(1).text()).toBe('Last 5 minutes'); + expect(wrapper.find('button').at(2).text()).toBe('Last 15 minutes'); + expect(wrapper.find('button').at(3).text()).toBe('Last 30 minutes'); + expect(wrapper.find('button').at(4).text()).toBe('Last 1 hour'); + + wrapper.find('button').at(1).simulate('click'); }); }); - diff --git a/webapp/__tests__/NameSelector.spec.js b/webapp/__tests__/NameSelector.spec.js index 29800a0362..407eeb9bf4 100644 --- a/webapp/__tests__/NameSelector.spec.js +++ b/webapp/__tests__/NameSelector.spec.js @@ -1,12 +1,12 @@ -import React from "react"; -import renderer from "react-test-renderer"; +import React from 'react'; +import renderer from 'react-test-renderer'; -import { configure, shallow, mount, render } from "enzyme"; -import { Provider } from "react-redux"; -import configureMockStore from "redux-mock-store"; -import Adapter from "enzyme-adapter-react-16"; +import { configure, shallow, mount, render } from 'enzyme'; +import { Provider } from 'react-redux'; +import configureMockStore from 'redux-mock-store'; +import Adapter from 'enzyme-adapter-react-16'; -import NameSelector from "../javascript/components/NameSelector"; +import NameSelector from '../javascript/components/NameSelector'; const mockStore = configureMockStore(); configure({ adapter: new Adapter() }); @@ -14,12 +14,12 @@ configure({ adapter: new Adapter() }); const store = mockStore({}); const props = { - query: "hotrod.golang.customer", - names: ["hotrod.golang.customer", "hotrod.golang.driver"], + query: 'hotrod.golang.customer', + names: ['hotrod.golang.customer', 'hotrod.golang.driver'], }; -describe("NameSelector", () => { - it("render correctly NameSelector component", () => { +describe('NameSelector', () => { + it('render correctly NameSelector component', () => { const NameSelectorComponent = renderer .create( @@ -29,15 +29,15 @@ describe("NameSelector", () => { .toJSON(); expect(NameSelectorComponent).toMatchSnapshot(); }); - it("When the name in the Applications dropdown menu changes, the page re-renders", () => { + it('When the name in the Applications dropdown menu changes, the page re-renders', () => { const wrapper = mount( ); - wrapper.find("select").simulate("mouseDown"); - expect(wrapper.find("option").length).toEqual(3); - wrapper.find("option").at(2).simulate("click", null); + wrapper.find('select').simulate('mouseDown'); + expect(wrapper.find('option').length).toEqual(3); + wrapper.find('option').at(2).simulate('click', null); }); }); diff --git a/webapp/__tests__/ProfilerHeader.spec.js b/webapp/__tests__/ProfilerHeader.spec.js index 7990560e48..8b9f6e4391 100644 --- a/webapp/__tests__/ProfilerHeader.spec.js +++ b/webapp/__tests__/ProfilerHeader.spec.js @@ -1,24 +1,24 @@ -import React from "react"; -import renderer from "react-test-renderer"; +import React from 'react'; +import renderer from 'react-test-renderer'; -import {configure, shallow} from "enzyme"; -import Adapter from "enzyme-adapter-react-16"; +import { configure, shallow } from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; -import ProfilerHeader from "../javascript/components/ProfilerHeader.jsx"; +import ProfilerHeader from '../javascript/components/ProfilerHeader.jsx'; -configure({adapter: new Adapter()}); +configure({ adapter: new Adapter() }); -describe("ProfilerHeader", () => { - it("render correctly ProfilerHeader component", () => { - const ProfilerHeaderComponent = renderer.create ().toJSON(); - expect(ProfilerHeaderComponent).toMatchSnapshot(); - }); - it("When user types in the search bar the state updates with the search term", () => { - const wrapper = shallow (); - const input = wrapper.find("input"); - input.props.value = 'ProfilerHeader' - expect(input.props.value).toEqual("ProfilerHeader"); - - }); +describe('ProfilerHeader', () => { + it('render correctly ProfilerHeader component', () => { + const ProfilerHeaderComponent = renderer + .create() + .toJSON(); + expect(ProfilerHeaderComponent).toMatchSnapshot(); + }); + it('When user types in the search bar the state updates with the search term', () => { + const wrapper = shallow(); + const input = wrapper.find('input'); + input.props.value = 'ProfilerHeader'; + expect(input.props.value).toEqual('ProfilerHeader'); + }); }); - diff --git a/webapp/__tests__/RefreshButton.spec.js b/webapp/__tests__/RefreshButton.spec.js index 559db9786b..cfaea2ea3d 100644 --- a/webapp/__tests__/RefreshButton.spec.js +++ b/webapp/__tests__/RefreshButton.spec.js @@ -1,30 +1,25 @@ -import React from "react"; -import renderer from "react-test-renderer"; +import React from 'react'; +import renderer from 'react-test-renderer'; -import { configure, mount , shallow,render} from "enzyme"; -import Adapter from "enzyme-adapter-react-16"; +import { configure, mount, shallow, render } from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; -import RefreshButton from "../javascript/components/RefreshButton.jsx"; +import RefreshButton from '../javascript/components/RefreshButton.jsx'; -jest.mock("react-redux", () => ({ - useDispatch: () => jest.fn() - })); +jest.mock('react-redux', () => ({ + useDispatch: () => jest.fn(), +})); configure({ adapter: new Adapter() }); -describe("RefreshButton", () => { - it("render correctly RefreshButton component", () => { - const RefreshButtonComponent = renderer - .create( ) - .toJSON(); - expect(RefreshButtonComponent).toMatchSnapshot(); - }); - it("When refresh button is clicked, Flamegraph component should update", () => { - const wrapper = shallow( - - ); - expect(wrapper.find("button").length).toEqual(1); - wrapper.find("button").simulate("click"); +describe('RefreshButton', () => { + it('render correctly RefreshButton component', () => { + const RefreshButtonComponent = renderer.create().toJSON(); + expect(RefreshButtonComponent).toMatchSnapshot(); + }); + it('When refresh button is clicked, Flamegraph component should update', () => { + const wrapper = shallow(); + expect(wrapper.find('button').length).toEqual(1); + wrapper.find('button').simulate('click'); }); }); - diff --git a/webapp/__tests__/ShortcutsModal.spec.js b/webapp/__tests__/ShortcutsModal.spec.js index b4d02bcf95..af939fa120 100644 --- a/webapp/__tests__/ShortcutsModal.spec.js +++ b/webapp/__tests__/ShortcutsModal.spec.js @@ -1,20 +1,20 @@ -import React from "react"; -import { configure, mount } from "enzyme"; -import { Provider } from "react-redux"; -import configureMockStore from "redux-mock-store"; -import Adapter from "enzyme-adapter-react-16"; -import { ShortcutProvider } from "react-keybind"; +import React from 'react'; +import { configure, mount } from 'enzyme'; +import { Provider } from 'react-redux'; +import configureMockStore from 'redux-mock-store'; +import Adapter from 'enzyme-adapter-react-16'; +import { ShortcutProvider } from 'react-keybind'; -import ShortcutsModal from "../javascript/components/ShortcutsModal"; -import Sidebar from "../javascript/components/Sidebar"; +import ShortcutsModal from '../javascript/components/ShortcutsModal'; +import Sidebar from '../javascript/components/Sidebar'; const mockStore = configureMockStore(); configure({ adapter: new Adapter() }); const store = mockStore({}); -describe("ShortcutsModal", () => { - it("When shortcuts are pressed, a shortcuts modal should appears", () => { +describe('ShortcutsModal', () => { + it('When shortcuts are pressed, a shortcuts modal should appears', () => { const wrapper = mount( @@ -23,9 +23,7 @@ describe("ShortcutsModal", () => { ); - wrapper.find("#tests-shortcuts-btn").last().simulate("click"); + wrapper.find('#tests-shortcuts-btn').last().simulate('click'); expect(wrapper.find(ShortcutsModal).length).toBe(1); - }); }); - diff --git a/webapp/__tests__/color.spec.js b/webapp/__tests__/color.spec.js index 30580f142f..ef5296c585 100644 --- a/webapp/__tests__/color.spec.js +++ b/webapp/__tests__/color.spec.js @@ -1,17 +1,17 @@ -import {colorBasedOnDiff} from '../javascript/components/FlameGraph/FlameGraphComponent/color'; +import { colorBasedOnDiff } from '../javascript/components/FlameGraph/FlameGraphComponent/color'; describe.each([ - [ -300, 100, 'rgba(0, 200, 0, 0.8)' ], - [ -200, 100, 'rgba(0, 200, 0, 0.8)' ], - [ -100, 100, 'rgba(0, 200, 0, 0.8)' ], - [ -50, 100, 'rgba(59, 200, 59, 0.8)' ], - [ 0, 100, 'rgba(200, 200, 200, 0.8)' ], - [ 50, 100, 'rgba(200, 59, 59, 0.8)' ], - [ 100, 100, 'rgba(200, 0, 0, 0.8)' ], - [ 200, 100, 'rgba(200, 0, 0, 0.8)' ], - [ 300, 100, 'rgba(200, 0, 0, 0.8)' ], + [-300, 100, 'rgba(0, 200, 0, 0.8)'], + [-200, 100, 'rgba(0, 200, 0, 0.8)'], + [-100, 100, 'rgba(0, 200, 0, 0.8)'], + [-50, 100, 'rgba(59, 200, 59, 0.8)'], + [0, 100, 'rgba(200, 200, 200, 0.8)'], + [50, 100, 'rgba(200, 59, 59, 0.8)'], + [100, 100, 'rgba(200, 0, 0, 0.8)'], + [200, 100, 'rgba(200, 0, 0, 0.8)'], + [300, 100, 'rgba(200, 0, 0, 0.8)'], ])('.colorBasedOnDiff(%i, %i)', (a, b, expected) => { it(`returns ${expected}`, () => { - expect(colorBasedOnDiff(a,b,0.8).toString()).toBe(expected); + expect(colorBasedOnDiff(a, b, 0.8).toString()).toBe(expected); }); }); diff --git a/webapp/__tests__/fitMode.spec.js b/webapp/__tests__/fitMode.spec.js index 494e7cf0e8..82fa9727be 100644 --- a/webapp/__tests__/fitMode.spec.js +++ b/webapp/__tests__/fitMode.spec.js @@ -1,24 +1,25 @@ import { fitToCanvasRect, FitModes } from '../javascript/util/fitMode'; - describe('fitToCanvasRect', () => { describe('HEAD', () => { - it("always returns fullText", () => { + it('always returns fullText', () => { const mode = FitModes.HEAD; const charSize = 1; const rectWidth = 10; - const fullText = "full_long_text"; - const shortText = "short_text"; + const fullText = 'full_long_text'; + const shortText = 'short_text'; const shortTextWidth = shortText.length * charSize; - expect(fitToCanvasRect({ - mode, - charSize, - rectWidth, - fullText, - shortText, - shortTextWidth - })).toMatchObject({ + expect( + fitToCanvasRect({ + mode, + charSize, + rectWidth, + fullText, + shortText, + shortTextWidth, + }) + ).toMatchObject({ mode, text: fullText, marginLeft: 3, @@ -26,67 +27,73 @@ describe('fitToCanvasRect', () => { }); }); - describe("TAIL", () => { - it("returns full text if it CAN fit", () => { + describe('TAIL', () => { + it('returns full text if it CAN fit', () => { const mode = FitModes.TAIL; const charSize = 1; const rectWidth = 99; - const fullText = "full_long_text"; - const shortText = "short_text"; + const fullText = 'full_long_text'; + const shortText = 'short_text'; const shortTextWidth = shortText.length * charSize; - expect(fitToCanvasRect({ - mode, - charSize, - rectWidth, - fullText, - shortText, - shortTextWidth - })).toMatchObject({ + expect( + fitToCanvasRect({ + mode, + charSize, + rectWidth, + fullText, + shortText, + shortTextWidth, + }) + ).toMatchObject({ mode, text: fullText, marginLeft: 3, }); }); - it("returns short text with if it CAN fit short text", () => { + it('returns short text with if it CAN fit short text', () => { const mode = FitModes.TAIL; const charSize = 1; const rectWidth = 10; - const fullText = "full_long_text"; - const shortText = "short_text"; + const fullText = 'full_long_text'; + const shortText = 'short_text'; const shortTextWidth = shortText.length * charSize; - expect(fitToCanvasRect({ - mode, - charSize, - rectWidth, - fullText, - shortText, - shortTextWidth - })).toMatchObject({ + expect( + fitToCanvasRect({ + mode, + charSize, + rectWidth, + fullText, + shortText, + shortTextWidth, + }) + ).toMatchObject({ mode, text: shortText, marginLeft: 3, }); }); - it("returns short text with negative margin BOTH short and long CAN NOT fit", () => { + it('returns short text with negative margin BOTH short and long CAN NOT fit', () => { const mode = FitModes.TAIL; const charSize = 1; const rectWidth = 10; - const fullText = "full_long_text"; // 14 - const shortText = "short_text_"; // 11 + const fullText = 'full_long_text'; // 14 + const shortText = 'short_text_'; // 11 const shortTextWidth = shortText.length * charSize; - expect(fitToCanvasRect({ - mode, - charSize, - rectWidth, - fullText, - shortText, - shortTextWidth - })).toMatchObject({ + expect( + fitToCanvasRect({ + mode, + charSize, + rectWidth, + fullText, + shortText, + shortTextWidth, + }) + ).toMatchObject({ mode, text: shortText, marginLeft: -4, diff --git a/webapp/__tests__/format.spec.js b/webapp/__tests__/format.spec.js index 7d734ad7e4..72d3470fc4 100644 --- a/webapp/__tests__/format.spec.js +++ b/webapp/__tests__/format.spec.js @@ -1,5 +1,4 @@ -import {DurationFormatter} from '../javascript/util/format'; - +import { DurationFormatter } from '../javascript/util/format'; describe('DurationFormatter', () => { it('correctly formats duration', () => { diff --git a/webapp/javascript/components/CheckIcon.jsx b/webapp/javascript/components/CheckIcon.jsx index 0ce231056f..fb401c14db 100644 --- a/webapp/javascript/components/CheckIcon.jsx +++ b/webapp/javascript/components/CheckIcon.jsx @@ -1,4 +1,4 @@ -import React from "react"; +import React from 'react'; // https://www.svgrepo.com/vectors/check/ export default function CheckIcon() { diff --git a/webapp/javascript/components/ComparisonApp.jsx b/webapp/javascript/components/ComparisonApp.jsx index 2e44e92b1f..0813d3ae02 100644 --- a/webapp/javascript/components/ComparisonApp.jsx +++ b/webapp/javascript/components/ComparisonApp.jsx @@ -1,14 +1,14 @@ -import React, { useEffect, useRef } from "react"; -import { connect } from "react-redux"; -import "react-dom"; - -import { bindActionCreators } from "redux"; -import FlameGraphRenderer from "./FlameGraph"; -import TimelineChartWrapper from "./TimelineChartWrapper"; -import Header from "./Header"; -import Footer from "./Footer"; -import { buildRenderURL } from "../util/updateRequests"; -import { fetchNames, fetchTimeline } from "../redux/actions"; +import React, { useEffect, useRef } from 'react'; +import { connect } from 'react-redux'; +import 'react-dom'; + +import { bindActionCreators } from 'redux'; +import FlameGraphRenderer from './FlameGraph'; +import TimelineChartWrapper from './TimelineChartWrapper'; +import Header from './Header'; +import Footer from './Footer'; +import { buildRenderURL } from '../util/updateRequests'; +import { fetchNames, fetchTimeline } from '../redux/actions'; // See docs here: https://github.com/flot/flot/blob/master/API.md diff --git a/webapp/javascript/components/ComparisonDiffApp.jsx b/webapp/javascript/components/ComparisonDiffApp.jsx index f6f3cea15e..c642b57bf6 100644 --- a/webapp/javascript/components/ComparisonDiffApp.jsx +++ b/webapp/javascript/components/ComparisonDiffApp.jsx @@ -1,12 +1,12 @@ -import React, { useEffect, useRef } from "react"; -import { connect } from "react-redux"; -import { bindActionCreators } from "redux"; -import FlameGraphRenderer from "./FlameGraph"; -import Header from "./Header"; -import Footer from "./Footer"; -import TimelineChartWrapper from "./TimelineChartWrapper"; -import { buildDiffRenderURL } from "../util/updateRequests"; -import { fetchNames, fetchTimeline } from "../redux/actions"; +import React, { useEffect, useRef } from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import FlameGraphRenderer from './FlameGraph'; +import Header from './Header'; +import Footer from './Footer'; +import TimelineChartWrapper from './TimelineChartWrapper'; +import { buildDiffRenderURL } from '../util/updateRequests'; +import { fetchNames, fetchTimeline } from '../redux/actions'; function ComparisonDiffApp(props) { const { actions, diffRenderURL } = props; diff --git a/webapp/javascript/components/CustomDatePicker.jsx b/webapp/javascript/components/CustomDatePicker.jsx index 5fad34ff4c..1058f2cd7d 100644 --- a/webapp/javascript/components/CustomDatePicker.jsx +++ b/webapp/javascript/components/CustomDatePicker.jsx @@ -1,9 +1,9 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect } from 'react'; // we import moment/src/moment instead of moment because we don't want to load locales -import moment from "moment/src/moment"; -import { useSelector } from "react-redux"; -import DatePicker from "react-datepicker"; -import { readableRange, formatAsOBject } from "../util/formatDate"; +import moment from 'moment/src/moment'; +import { useSelector } from 'react-redux'; +import DatePicker from 'react-datepicker'; +import { readableRange, formatAsOBject } from '../util/formatDate'; function CustomDatePicker({ setRange, dispatch, setDateRange }) { const from = useSelector((state) => state.from); @@ -69,7 +69,7 @@ function CustomDatePicker({ setRange, dispatch, setDateRange }) { dateFormat="yyyy-MM-dd hh:mm aa" /> - {warning &&

Warning: invalid date Range

} + {warning &&

Warning: invalid date Range

}
Export Flamegraph