chore: Upgrade Webpack to v5 (#16701)
* Upgrade Webpack to v5 * Remove mapbox hack * Replace url-loaders and file-loaders with asset modules * Remove 1 rule * Change --colors to --color * Remove invalid option (--no-progress) * Remove url-loader, bump plugin * Fix AnnotationLayer formula check * Remove redundant tests * Bump cypress packages * Remove old comment * Fix tests * Remove checks for number of scripts in markdown test * Cosmetic changes * Add tests * Fix test * Fix test * Fixes test warnings * disable flaky test Co-authored-by: Ville Brofeldt <ville.v.brofeldt@gmail.com> Co-authored-by: Michael S. Molina <michael.s.molina@gmail.com>
This commit is contained in:
parent
9b17e86b44
commit
486e0d412c
|
|
@ -70,7 +70,7 @@ build-assets() {
|
||||||
cd "$GITHUB_WORKSPACE/superset-frontend"
|
cd "$GITHUB_WORKSPACE/superset-frontend"
|
||||||
|
|
||||||
say "::group::Build static assets"
|
say "::group::Build static assets"
|
||||||
npm run build -- --no-progress
|
npm run build
|
||||||
say "::endgroup::"
|
say "::endgroup::"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,7 +82,7 @@ build-instrumented-assets() {
|
||||||
if [[ -f "$ASSETS_MANIFEST" ]]; then
|
if [[ -f "$ASSETS_MANIFEST" ]]; then
|
||||||
echo 'Skip frontend build because instrumented static assets already exist.'
|
echo 'Skip frontend build because instrumented static assets already exist.'
|
||||||
else
|
else
|
||||||
npm run build-instrumented -- --no-progress
|
npm run build-instrumented
|
||||||
cache-save instrumented-assets
|
cache-save instrumented-assets
|
||||||
fi
|
fi
|
||||||
say "::endgroup::"
|
say "::endgroup::"
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,7 @@ describe('Dashboard edit markdown', () => {
|
||||||
cy.visit(TABBED_DASHBOARD);
|
cy.visit(TABBED_DASHBOARD);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should load AceEditor on demand', () => {
|
it('should add markdown component to dashboard', () => {
|
||||||
let numScripts = 0;
|
|
||||||
cy.get('script').then(nodes => {
|
|
||||||
numScripts = nodes.length;
|
|
||||||
});
|
|
||||||
cy.get('[data-test="dashboard-header"]')
|
cy.get('[data-test="dashboard-header"]')
|
||||||
.find('[aria-label="edit-alt"]')
|
.find('[aria-label="edit-alt"]')
|
||||||
.click();
|
.click();
|
||||||
|
|
@ -37,11 +33,6 @@ describe('Dashboard edit markdown', () => {
|
||||||
cy.get('[data-test="dashboard-header"]')
|
cy.get('[data-test="dashboard-header"]')
|
||||||
.find('[aria-label="more-horiz"]')
|
.find('[aria-label="more-horiz"]')
|
||||||
.click();
|
.click();
|
||||||
cy.get('script').then(nodes => {
|
|
||||||
// load 5 new script chunks for css editor
|
|
||||||
expect(nodes.length).to.greaterThan(numScripts);
|
|
||||||
numScripts = nodes.length;
|
|
||||||
});
|
|
||||||
cy.get('[data-test="grid-row-background--transparent"]')
|
cy.get('[data-test="grid-row-background--transparent"]')
|
||||||
.first()
|
.first()
|
||||||
.as('component-background-first');
|
.as('component-background-first');
|
||||||
|
|
@ -49,11 +40,6 @@ describe('Dashboard edit markdown', () => {
|
||||||
drag('[data-test="new-component"]', 'Markdown').to(
|
drag('[data-test="new-component"]', 'Markdown').to(
|
||||||
'@component-background-first',
|
'@component-background-first',
|
||||||
);
|
);
|
||||||
cy.get('script').then(nodes => {
|
|
||||||
// load more scripts for markdown editor
|
|
||||||
expect(nodes.length).to.greaterThan(numScripts);
|
|
||||||
numScripts = nodes.length;
|
|
||||||
});
|
|
||||||
cy.get('[data-test="dashboard-markdown-editor"]')
|
cy.get('[data-test="dashboard-markdown-editor"]')
|
||||||
.should(
|
.should(
|
||||||
'have.text',
|
'have.text',
|
||||||
|
|
@ -75,12 +61,6 @@ describe('Dashboard edit markdown', () => {
|
||||||
|
|
||||||
cy.get('[data-test="dashboard-markdown-editor"]').contains('Test resize');
|
cy.get('[data-test="dashboard-markdown-editor"]').contains('Test resize');
|
||||||
|
|
||||||
// entering edit mode does not add new scripts
|
|
||||||
// (though scripts may still be removed by others)
|
|
||||||
cy.get('script').then(nodes => {
|
|
||||||
expect(nodes.length).to.greaterThan(numScripts);
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.get('@component-background-first').click('right');
|
cy.get('@component-background-first').click('right');
|
||||||
cy.get('[data-test="dashboard-component-chart-holder"]')
|
cy.get('[data-test="dashboard-component-chart-holder"]')
|
||||||
.find('.ace_content')
|
.find('.ace_content')
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,6 @@ describe('AdhocFilters', () => {
|
||||||
cy.verifySliceSuccess({ waitAlias: '@postJson' });
|
cy.verifySliceSuccess({ waitAlias: '@postJson' });
|
||||||
});
|
});
|
||||||
|
|
||||||
xit('Should not load mathjs when not needed', () => {
|
|
||||||
cy.get('script[src*="mathjs"]').should('have.length', 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
let numScripts = 0;
|
let numScripts = 0;
|
||||||
|
|
||||||
xit('Should load AceEditor scripts when needed', () => {
|
xit('Should load AceEditor scripts when needed', () => {
|
||||||
|
|
|
||||||
|
|
@ -105,9 +105,6 @@ describe('VizType control', () => {
|
||||||
cy.get('[role="button"]').contains('Line Chart').click();
|
cy.get('[role="button"]').contains('Line Chart').click();
|
||||||
cy.get('button').contains('Select').click();
|
cy.get('button').contains('Select').click();
|
||||||
|
|
||||||
// should load mathjs for line chart
|
|
||||||
cy.get('script[src*="mathjs"]').should('have.length', 1);
|
|
||||||
|
|
||||||
cy.get('button[data-test="run-query-button"]').click();
|
cy.get('button[data-test="run-query-button"]').click();
|
||||||
cy.verifySliceSuccess({
|
cy.verifySliceSuccess({
|
||||||
waitAlias: '@lineChartData',
|
waitAlias: '@lineChartData',
|
||||||
|
|
|
||||||
|
|
@ -32,14 +32,6 @@ describe('Visualization > Line', () => {
|
||||||
cy.get('.ant-alert-warning').contains(`Metrics: cannot be empty`);
|
cy.get('.ant-alert-warning').contains(`Metrics: cannot be empty`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should preload mathjs', () => {
|
|
||||||
cy.get('script[src*="mathjs"]').should('have.length', 1);
|
|
||||||
cy.contains('Add annotation layer').scrollIntoView().click();
|
|
||||||
// should not load additional mathjs
|
|
||||||
cy.get('script[src*="mathjs"]').should('have.length', 1);
|
|
||||||
cy.contains('Layer configuration');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not show validator error when metric added', () => {
|
it('should not show validator error when metric added', () => {
|
||||||
const formData = { ...LINE_CHART_DEFAULTS, metrics: [] };
|
const formData = { ...LINE_CHART_DEFAULTS, metrics: [] };
|
||||||
cy.visitChartByParams(JSON.stringify(formData));
|
cy.visitChartByParams(JSON.stringify(formData));
|
||||||
|
|
@ -85,7 +77,6 @@ describe('Visualization > Line', () => {
|
||||||
const formData = { ...LINE_CHART_DEFAULTS, metrics: [NUM_METRIC] };
|
const formData = { ...LINE_CHART_DEFAULTS, metrics: [NUM_METRIC] };
|
||||||
cy.visitChartByParams(JSON.stringify(formData));
|
cy.visitChartByParams(JSON.stringify(formData));
|
||||||
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
|
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
|
||||||
cy.get('script[src*="mathjs"]').should('have.length', 1);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work with groupby', () => {
|
it('should work with groupby', () => {
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -7,18 +7,18 @@
|
||||||
"cypress-run-chrome": "cypress run --browser chrome --headless",
|
"cypress-run-chrome": "cypress run --browser chrome --headless",
|
||||||
"cypress-debug": "cypress open --config watchForFileChanges=true"
|
"cypress-debug": "cypress open --config watchForFileChanges=true"
|
||||||
},
|
},
|
||||||
"author": "Apcahe",
|
"author": "Apache",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cypress/code-coverage": "^3.9.2",
|
"@cypress/code-coverage": "^3.9.11",
|
||||||
"@superset-ui/core": "^0.17.42",
|
"@superset-ui/core": "^0.18.4",
|
||||||
"react-dom": "^16.13.0",
|
"react-dom": "^16.13.0",
|
||||||
"rison": "^0.1.1",
|
"rison": "^0.1.1",
|
||||||
"shortid": "^2.2.15"
|
"shortid": "^2.2.15"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"cypress": "^6.3.0",
|
"cypress": "^6.3.0",
|
||||||
"eslint-plugin-cypress": "^2.11.2"
|
"eslint-plugin-cypress": "^2.12.1"
|
||||||
},
|
},
|
||||||
"nyc": {
|
"nyc": {
|
||||||
"reporter": [
|
"reporter": [
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -12,12 +12,12 @@
|
||||||
"test": "cross-env NODE_ENV=test jest",
|
"test": "cross-env NODE_ENV=test jest",
|
||||||
"type": "tsc --noEmit",
|
"type": "tsc --noEmit",
|
||||||
"cover": "cross-env NODE_ENV=test jest --coverage",
|
"cover": "cross-env NODE_ENV=test jest --coverage",
|
||||||
"dev": "webpack --mode=development --colors --debug --watch",
|
"dev": "webpack --mode=development --color --watch",
|
||||||
"dev-server": "cross-env NODE_ENV=development BABEL_ENV=development node --max_old_space_size=4096 ./node_modules/webpack-dev-server/bin/webpack-dev-server.js --mode=development",
|
"dev-server": "cross-env NODE_ENV=development BABEL_ENV=development node --max_old_space_size=4096 ./node_modules/webpack-dev-server/bin/webpack-dev-server.js --mode=development",
|
||||||
"prod": "npm run build",
|
"prod": "npm run build",
|
||||||
"build-dev": "cross-env NODE_OPTIONS=--max_old_space_size=8192 NODE_ENV=development webpack --mode=development --colors",
|
"build-dev": "cross-env NODE_OPTIONS=--max_old_space_size=8192 NODE_ENV=development webpack --mode=development --color",
|
||||||
"build-instrumented": "cross-env NODE_ENV=development BABEL_ENV=instrumented webpack --mode=development --colors",
|
"build-instrumented": "cross-env NODE_ENV=development BABEL_ENV=instrumented webpack --mode=development --color",
|
||||||
"build": "cross-env NODE_OPTIONS=--max_old_space_size=8192 NODE_ENV=production BABEL_ENV=\"${BABEL_ENV:=production}\" webpack --mode=production --colors",
|
"build": "cross-env NODE_OPTIONS=--max_old_space_size=8192 NODE_ENV=production BABEL_ENV=\"${BABEL_ENV:=production}\" webpack --mode=production --color",
|
||||||
"lint": "eslint --ignore-path=.eslintignore --ext .js,.jsx,.ts,.tsx . && npm run type",
|
"lint": "eslint --ignore-path=.eslintignore --ext .js,.jsx,.ts,.tsx . && npm run type",
|
||||||
"prettier-check": "prettier --check '{src,stylesheets}/**/*.{css,less,sass,scss}'",
|
"prettier-check": "prettier --check '{src,stylesheets}/**/*.{css,less,sass,scss}'",
|
||||||
"lint-fix": "eslint --fix --ignore-path=.eslintignore --ext .js,.jsx,.ts,tsx . && npm run clean-css && npm run type",
|
"lint-fix": "eslint --fix --ignore-path=.eslintignore --ext .js,.jsx,.ts,tsx . && npm run clean-css && npm run type",
|
||||||
|
|
@ -117,7 +117,7 @@
|
||||||
"fuse.js": "^6.4.6",
|
"fuse.js": "^6.4.6",
|
||||||
"geolib": "^2.0.24",
|
"geolib": "^2.0.24",
|
||||||
"global-box": "^1.2.0",
|
"global-box": "^1.2.0",
|
||||||
"html-webpack-plugin": "^4.5.1",
|
"html-webpack-plugin": "^5.3.2",
|
||||||
"immer": "^9.0.6",
|
"immer": "^9.0.6",
|
||||||
"immutable": "^4.0.0-rc.12",
|
"immutable": "^4.0.0-rc.12",
|
||||||
"interweave": "^11.2.0",
|
"interweave": "^11.2.0",
|
||||||
|
|
@ -129,7 +129,7 @@
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"match-sorter": "^6.1.0",
|
"match-sorter": "^6.1.0",
|
||||||
"mathjs": "^8.0.1",
|
"math-expression-evaluator": "^1.3.8",
|
||||||
"memoize-one": "^5.1.1",
|
"memoize-one": "^5.1.1",
|
||||||
"moment": "^2.26.0",
|
"moment": "^2.26.0",
|
||||||
"moment-timezone": "^0.5.33",
|
"moment-timezone": "^0.5.33",
|
||||||
|
|
@ -210,7 +210,7 @@
|
||||||
"@storybook/client-api": "^6.1.17",
|
"@storybook/client-api": "^6.1.17",
|
||||||
"@storybook/preset-typescript": "^3.0.0",
|
"@storybook/preset-typescript": "^3.0.0",
|
||||||
"@storybook/react": "^6.1.20",
|
"@storybook/react": "^6.1.20",
|
||||||
"@svgr/webpack": "^5.4.0",
|
"@svgr/webpack": "^5.5.0",
|
||||||
"@testing-library/dom": "^7.29.4",
|
"@testing-library/dom": "^7.29.4",
|
||||||
"@testing-library/jest-dom": "^5.11.6",
|
"@testing-library/jest-dom": "^5.11.6",
|
||||||
"@testing-library/react": "^11.2.0",
|
"@testing-library/react": "^11.2.0",
|
||||||
|
|
@ -252,20 +252,19 @@
|
||||||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||||
"babel-plugin-jsx-remove-data-test-id": "^2.1.3",
|
"babel-plugin-jsx-remove-data-test-id": "^2.1.3",
|
||||||
"babel-plugin-lodash": "^3.3.4",
|
"babel-plugin-lodash": "^3.3.4",
|
||||||
"cache-loader": "^1.2.2",
|
"copy-webpack-plugin": "^9.0.1",
|
||||||
"clean-webpack-plugin": "^3.0.0",
|
|
||||||
"copy-webpack-plugin": "^6.0.3",
|
|
||||||
"cross-env": "^5.2.0",
|
"cross-env": "^5.2.0",
|
||||||
"css-loader": "^1.0.0",
|
"css-loader": "^6.2.0",
|
||||||
|
"css-minimizer-webpack-plugin": "^3.0.2",
|
||||||
"enzyme": "^3.10.0",
|
"enzyme": "^3.10.0",
|
||||||
"enzyme-adapter-react-16": "^1.14.0",
|
"enzyme-adapter-react-16": "^1.14.0",
|
||||||
"eslint": "^7.17.0",
|
"eslint": "^7.17.0",
|
||||||
"eslint-config-airbnb": "^18.2.1",
|
"eslint-config-airbnb": "^18.2.1",
|
||||||
"eslint-config-prettier": "^7.1.0",
|
"eslint-config-prettier": "^7.1.0",
|
||||||
"eslint-import-resolver-typescript": "^2.3.0",
|
"eslint-import-resolver-typescript": "^2.5.0",
|
||||||
"eslint-import-resolver-webpack": "^0.13.0",
|
"eslint-import-resolver-webpack": "^0.13.1",
|
||||||
"eslint-plugin-cypress": "^2.11.2",
|
"eslint-plugin-cypress": "^2.11.2",
|
||||||
"eslint-plugin-import": "^2.22.1",
|
"eslint-plugin-import": "^2.24.2",
|
||||||
"eslint-plugin-jest": "^24.1.3",
|
"eslint-plugin-jest": "^24.1.3",
|
||||||
"eslint-plugin-jest-dom": "^3.6.5",
|
"eslint-plugin-jest-dom": "^3.6.5",
|
||||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||||
|
|
@ -277,9 +276,9 @@
|
||||||
"exports-loader": "^0.7.0",
|
"exports-loader": "^0.7.0",
|
||||||
"fetch-mock": "^7.7.3",
|
"fetch-mock": "^7.7.3",
|
||||||
"file-loader": "^6.0.0",
|
"file-loader": "^6.0.0",
|
||||||
"fork-ts-checker-webpack-plugin": "^0.4.9",
|
"fork-ts-checker-webpack-plugin": "^6.3.3",
|
||||||
"ignore-styles": "^5.0.1",
|
"ignore-styles": "^5.0.1",
|
||||||
"imports-loader": "^0.7.1",
|
"imports-loader": "^3.0.0",
|
||||||
"jest": "^26.6.3",
|
"jest": "^26.6.3",
|
||||||
"jest-environment-enzyme": "^7.1.2",
|
"jest-environment-enzyme": "^7.1.2",
|
||||||
"jest-enzyme": "^7.1.2",
|
"jest-enzyme": "^7.1.2",
|
||||||
|
|
@ -287,32 +286,31 @@
|
||||||
"jsdom": "^16.4.0",
|
"jsdom": "^16.4.0",
|
||||||
"less": "^3.12.2",
|
"less": "^3.12.2",
|
||||||
"less-loader": "^5.0.0",
|
"less-loader": "^5.0.0",
|
||||||
"mini-css-extract-plugin": "^0.4.0",
|
"mini-css-extract-plugin": "^2.3.0",
|
||||||
"mock-socket": "^9.0.3",
|
"mock-socket": "^9.0.3",
|
||||||
"node-fetch": "^2.6.1",
|
"node-fetch": "^2.6.1",
|
||||||
"optimize-css-assets-webpack-plugin": "^5.0.1",
|
|
||||||
"po2json": "^0.4.5",
|
"po2json": "^0.4.5",
|
||||||
"prettier": "^2.2.1",
|
"prettier": "^2.2.1",
|
||||||
|
"process": "^0.11.10",
|
||||||
"react-test-renderer": "^16.9.0",
|
"react-test-renderer": "^16.9.0",
|
||||||
"redux-mock-store": "^1.5.4",
|
"redux-mock-store": "^1.5.4",
|
||||||
"sinon": "^9.0.2",
|
"sinon": "^9.0.2",
|
||||||
"source-map-support": "^0.5.16",
|
"source-map-support": "^0.5.16",
|
||||||
"speed-measure-webpack-plugin": "^1.2.3",
|
"speed-measure-webpack-plugin": "^1.5.0",
|
||||||
"storybook-addon-jsx": "^7.3.3",
|
"storybook-addon-jsx": "^7.3.3",
|
||||||
"storybook-addon-paddings": "^3.2.0",
|
"storybook-addon-paddings": "^3.2.0",
|
||||||
"style-loader": "^1.0.0",
|
"style-loader": "^3.2.1",
|
||||||
"thread-loader": "^1.2.0",
|
"thread-loader": "^3.0.4",
|
||||||
"transform-loader": "^0.2.3",
|
"transform-loader": "^0.2.4",
|
||||||
"ts-jest": "^26.4.2",
|
"ts-jest": "^26.4.2",
|
||||||
"ts-loader": "^8.0.7",
|
"ts-loader": "^9.2.5",
|
||||||
"typescript": "^4.1.6",
|
"typescript": "^4.1.6",
|
||||||
"url-loader": "^1.0.1",
|
"webpack": "^5.52.1",
|
||||||
"webpack": "^4.42.0",
|
"webpack-bundle-analyzer": "^4.4.2",
|
||||||
"webpack-bundle-analyzer": "^3.6.1",
|
"webpack-cli": "^4.8.0",
|
||||||
"webpack-cli": "^3.3.11",
|
"webpack-dev-server": "^4.2.0",
|
||||||
"webpack-dev-server": "^3.11.0",
|
"webpack-manifest-plugin": "^4.0.2",
|
||||||
"webpack-manifest-plugin": "^2.2.0",
|
"webpack-sources": "^3.2.0",
|
||||||
"webpack-sources": "^1.4.3",
|
|
||||||
"yargs": "^15.4.1"
|
"yargs": "^15.4.1"
|
||||||
},
|
},
|
||||||
"stylelint": {
|
"stylelint": {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import AntdIcon from '@ant-design/icons';
|
import AntdIcon from '@ant-design/icons';
|
||||||
import { styled } from '@superset-ui/core';
|
import { styled } from '@superset-ui/core';
|
||||||
import { ReactComponent as TransparentIcon } from 'images/icons/transparent.svg';
|
import TransparentIcon from 'images/icons/transparent.svg';
|
||||||
import IconType from './IconType';
|
import IconType from './IconType';
|
||||||
|
|
||||||
const AntdIconComponent = ({
|
const AntdIconComponent = ({
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import { t, styled } from '@superset-ui/core';
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { Empty } from 'src/common/components';
|
import { Empty } from 'src/common/components';
|
||||||
import Alert from 'src/components/Alert';
|
import Alert from 'src/components/Alert';
|
||||||
import { ReactComponent as EmptyImage } from 'images/empty.svg';
|
import EmptyImage from 'images/empty.svg';
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
import Button from 'src/components/Button';
|
import Button from 'src/components/Button';
|
||||||
import Icons from 'src/components/Icons';
|
import Icons from 'src/components/Icons';
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { CompactPicker } from 'react-color';
|
import { CompactPicker } from 'react-color';
|
||||||
import Button from 'src/components/Button';
|
import Button from 'src/components/Button';
|
||||||
import { parse as mathjsParse } from 'mathjs';
|
import mexp from 'math-expression-evaluator';
|
||||||
import {
|
import {
|
||||||
t,
|
t,
|
||||||
SupersetClient,
|
SupersetClient,
|
||||||
|
|
@ -204,15 +204,30 @@ export default class AnnotationLayer extends React.PureComponent {
|
||||||
return sources;
|
return sources;
|
||||||
}
|
}
|
||||||
|
|
||||||
isValidFormula(value, annotationType) {
|
isValidFormula(expression, annotationType) {
|
||||||
if (annotationType === ANNOTATION_TYPES.FORMULA) {
|
if (annotationType === ANNOTATION_TYPES.FORMULA) {
|
||||||
try {
|
try {
|
||||||
mathjsParse(value).compile().evaluate({ x: 0 });
|
const token = {
|
||||||
|
type: 3,
|
||||||
|
token: 'x',
|
||||||
|
show: 'x',
|
||||||
|
value: 'x',
|
||||||
|
};
|
||||||
|
|
||||||
|
// handle input like "y = x+1" instead of just "x+1"
|
||||||
|
const subexpressions = expression.split('=');
|
||||||
|
if (
|
||||||
|
subexpressions.length > 2 ||
|
||||||
|
(subexpressions[1] && !subexpressions[0].match(/^\s*[a-zA-Z]\w*\s*$/))
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mexp.eval(subexpressions[1] ?? subexpressions[0], [token], { x: 0 });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
isValidForm() {
|
isValidForm() {
|
||||||
|
|
@ -238,7 +253,7 @@ export default class AnnotationLayer extends React.PureComponent {
|
||||||
errors.push(validateNonEmpty(intervalEndColumn));
|
errors.push(validateNonEmpty(intervalEndColumn));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
errors.push(this.isValidFormula(value, annotationType));
|
errors.push(!this.isValidFormula(value, annotationType));
|
||||||
return !errors.filter(x => x).length;
|
return !errors.filter(x => x).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -442,7 +457,7 @@ export default class AnnotationLayer extends React.PureComponent {
|
||||||
value={value}
|
value={value}
|
||||||
onChange={this.handleValue}
|
onChange={this.handleValue}
|
||||||
validationErrors={
|
validationErrors={
|
||||||
this.isValidFormula(value, annotationType) ? ['Bad formula.'] : []
|
!this.isValidFormula(value, annotationType) ? ['Bad formula.'] : []
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -57,16 +57,18 @@ beforeAll(() => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('renders with default props', () => {
|
const waitForRender = (props?: any) =>
|
||||||
const { container } = render(<AnnotationLayer {...defaultProps} />);
|
waitFor(() => render(<AnnotationLayer {...defaultProps} {...props} />));
|
||||||
expect(container).toBeInTheDocument();
|
|
||||||
|
test('renders with default props', async () => {
|
||||||
|
await waitForRender();
|
||||||
expect(screen.getByRole('button', { name: 'Apply' })).toBeDisabled();
|
expect(screen.getByRole('button', { name: 'Apply' })).toBeDisabled();
|
||||||
expect(screen.getByRole('button', { name: 'OK' })).toBeDisabled();
|
expect(screen.getByRole('button', { name: 'OK' })).toBeDisabled();
|
||||||
expect(screen.getByRole('button', { name: 'Cancel' })).toBeEnabled();
|
expect(screen.getByRole('button', { name: 'Cancel' })).toBeEnabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('renders extra checkboxes when type is time series', () => {
|
test('renders extra checkboxes when type is time series', async () => {
|
||||||
render(<AnnotationLayer {...defaultProps} />);
|
await waitForRender();
|
||||||
expect(
|
expect(
|
||||||
screen.queryByRole('button', { name: 'Show Markers' }),
|
screen.queryByRole('button', { name: 'Show Markers' }),
|
||||||
).not.toBeInTheDocument();
|
).not.toBeInTheDocument();
|
||||||
|
|
@ -82,7 +84,7 @@ test('renders extra checkboxes when type is time series', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('enables apply and ok buttons', async () => {
|
test('enables apply and ok buttons', async () => {
|
||||||
render(<AnnotationLayer {...defaultProps} />);
|
await waitForRender();
|
||||||
userEvent.type(screen.getByLabelText('Name'), 'Test');
|
userEvent.type(screen.getByLabelText('Name'), 'Test');
|
||||||
userEvent.type(screen.getByLabelText('Formula'), '2x');
|
userEvent.type(screen.getByLabelText('Formula'), '2x');
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
|
|
@ -91,68 +93,47 @@ test('enables apply and ok buttons', async () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('triggers addAnnotationLayer when apply button is clicked', () => {
|
test('triggers addAnnotationLayer when apply button is clicked', async () => {
|
||||||
const addAnnotationLayer = jest.fn();
|
const addAnnotationLayer = jest.fn();
|
||||||
render(
|
await waitForRender({ name: 'Test', value: '2x', addAnnotationLayer });
|
||||||
<AnnotationLayer
|
|
||||||
{...defaultProps}
|
|
||||||
name="Test"
|
|
||||||
value="2x"
|
|
||||||
addAnnotationLayer={addAnnotationLayer}
|
|
||||||
/>,
|
|
||||||
);
|
|
||||||
userEvent.click(screen.getByRole('button', { name: 'Apply' }));
|
userEvent.click(screen.getByRole('button', { name: 'Apply' }));
|
||||||
expect(addAnnotationLayer).toHaveBeenCalled();
|
expect(addAnnotationLayer).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('triggers addAnnotationLayer and close when ok button is clicked', () => {
|
test('triggers addAnnotationLayer and close when ok button is clicked', async () => {
|
||||||
const addAnnotationLayer = jest.fn();
|
const addAnnotationLayer = jest.fn();
|
||||||
const close = jest.fn();
|
const close = jest.fn();
|
||||||
render(
|
await waitForRender({ name: 'Test', value: '2x', addAnnotationLayer, close });
|
||||||
<AnnotationLayer
|
|
||||||
{...defaultProps}
|
|
||||||
name="Test"
|
|
||||||
value="2x"
|
|
||||||
addAnnotationLayer={addAnnotationLayer}
|
|
||||||
close={close}
|
|
||||||
/>,
|
|
||||||
);
|
|
||||||
userEvent.click(screen.getByRole('button', { name: 'OK' }));
|
userEvent.click(screen.getByRole('button', { name: 'OK' }));
|
||||||
expect(addAnnotationLayer).toHaveBeenCalled();
|
expect(addAnnotationLayer).toHaveBeenCalled();
|
||||||
expect(close).toHaveBeenCalled();
|
expect(close).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('triggers close when cancel button is clicked', () => {
|
test('triggers close when cancel button is clicked', async () => {
|
||||||
const close = jest.fn();
|
const close = jest.fn();
|
||||||
render(<AnnotationLayer {...defaultProps} close={close} />);
|
await waitForRender({ close });
|
||||||
userEvent.click(screen.getByRole('button', { name: 'Cancel' }));
|
userEvent.click(screen.getByRole('button', { name: 'Cancel' }));
|
||||||
expect(close).toHaveBeenCalled();
|
expect(close).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('triggers removeAnnotationLayer and close when remove button is clicked', () => {
|
test('triggers removeAnnotationLayer and close when remove button is clicked', async () => {
|
||||||
const removeAnnotationLayer = jest.fn();
|
const removeAnnotationLayer = jest.fn();
|
||||||
const close = jest.fn();
|
const close = jest.fn();
|
||||||
render(
|
await waitForRender({
|
||||||
<AnnotationLayer
|
name: 'Test',
|
||||||
{...defaultProps}
|
value: '2x',
|
||||||
name="Test"
|
removeAnnotationLayer,
|
||||||
value="2x"
|
close,
|
||||||
removeAnnotationLayer={removeAnnotationLayer}
|
});
|
||||||
close={close}
|
|
||||||
/>,
|
|
||||||
);
|
|
||||||
userEvent.click(screen.getByRole('button', { name: 'Remove' }));
|
userEvent.click(screen.getByRole('button', { name: 'Remove' }));
|
||||||
expect(removeAnnotationLayer).toHaveBeenCalled();
|
expect(removeAnnotationLayer).toHaveBeenCalled();
|
||||||
expect(close).toHaveBeenCalled();
|
expect(close).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('renders chart options', async () => {
|
test('renders chart options', async () => {
|
||||||
render(
|
await waitForRender({
|
||||||
<AnnotationLayer
|
annotationType: ANNOTATION_TYPES_METADATA.EVENT.value,
|
||||||
{...defaultProps}
|
});
|
||||||
annotationType={ANNOTATION_TYPES_METADATA.EVENT.value}
|
|
||||||
/>,
|
|
||||||
);
|
|
||||||
userEvent.click(screen.getByText('2 option(s)'));
|
userEvent.click(screen.getByText('2 option(s)'));
|
||||||
userEvent.click(screen.getByText('Superset annotation'));
|
userEvent.click(screen.getByText('Superset annotation'));
|
||||||
expect(await screen.findByLabelText('Annotation layer')).toBeInTheDocument();
|
expect(await screen.findByLabelText('Annotation layer')).toBeInTheDocument();
|
||||||
|
|
@ -162,15 +143,12 @@ test('renders chart options', async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('keeps apply disabled when missing required fields', async () => {
|
test('keeps apply disabled when missing required fields', async () => {
|
||||||
render(
|
await waitForRender({
|
||||||
<AnnotationLayer
|
annotationType: ANNOTATION_TYPES_METADATA.EVENT.value,
|
||||||
{...defaultProps}
|
sourceType: 'Table',
|
||||||
annotationType={ANNOTATION_TYPES_METADATA.EVENT.value}
|
});
|
||||||
sourceType="Table"
|
userEvent.click(screen.getByText('1 option(s)'));
|
||||||
/>,
|
await waitFor(() => userEvent.click(screen.getByText('Chart A')));
|
||||||
);
|
|
||||||
userEvent.click(await screen.findByText('1 option(s)'));
|
|
||||||
userEvent.click(screen.getByText('Chart A'));
|
|
||||||
expect(
|
expect(
|
||||||
screen.getByText('Annotation Slice Configuration'),
|
screen.getByText('Annotation Slice Configuration'),
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
|
|
@ -188,3 +166,48 @@ test('keeps apply disabled when missing required fields', async () => {
|
||||||
|
|
||||||
expect(screen.getByRole('button', { name: 'Apply' })).toBeDisabled();
|
expect(screen.getByRole('button', { name: 'Apply' })).toBeDisabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.skip('Disable apply button if formula is incorrect', async () => {
|
||||||
|
// TODO: fix flaky test that passes locally but fails on CI
|
||||||
|
await waitForRender({ name: 'test' });
|
||||||
|
|
||||||
|
userEvent.clear(screen.getByLabelText('Formula'));
|
||||||
|
userEvent.type(screen.getByLabelText('Formula'), 'x+1');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByLabelText('Formula')).toHaveValue('x+1');
|
||||||
|
expect(screen.getByRole('button', { name: 'OK' })).toBeEnabled();
|
||||||
|
expect(screen.getByRole('button', { name: 'Apply' })).toBeEnabled();
|
||||||
|
});
|
||||||
|
|
||||||
|
userEvent.clear(screen.getByLabelText('Formula'));
|
||||||
|
userEvent.type(screen.getByLabelText('Formula'), 'y = x*2+1');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByLabelText('Formula')).toHaveValue('y = x*2+1');
|
||||||
|
expect(screen.getByRole('button', { name: 'OK' })).toBeEnabled();
|
||||||
|
expect(screen.getByRole('button', { name: 'Apply' })).toBeEnabled();
|
||||||
|
});
|
||||||
|
|
||||||
|
userEvent.clear(screen.getByLabelText('Formula'));
|
||||||
|
userEvent.type(screen.getByLabelText('Formula'), 'y+1');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByLabelText('Formula')).toHaveValue('y+1');
|
||||||
|
expect(screen.getByRole('button', { name: 'OK' })).toBeDisabled();
|
||||||
|
expect(screen.getByRole('button', { name: 'Apply' })).toBeDisabled();
|
||||||
|
});
|
||||||
|
|
||||||
|
userEvent.clear(screen.getByLabelText('Formula'));
|
||||||
|
userEvent.type(screen.getByLabelText('Formula'), 'x+');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByLabelText('Formula')).toHaveValue('x+');
|
||||||
|
expect(screen.getByRole('button', { name: 'OK' })).toBeDisabled();
|
||||||
|
expect(screen.getByRole('button', { name: 'Apply' })).toBeDisabled();
|
||||||
|
});
|
||||||
|
|
||||||
|
userEvent.clear(screen.getByLabelText('Formula'));
|
||||||
|
userEvent.type(screen.getByLabelText('Formula'), 'y = z+1');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByLabelText('Formula')).toHaveValue('y = z+1');
|
||||||
|
expect(screen.getByRole('button', { name: 'OK' })).toBeDisabled();
|
||||||
|
expect(screen.getByRole('button', { name: 'Apply' })).toBeDisabled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ export default class FilterBoxChartPlugin extends ChartPlugin {
|
||||||
controlPanel,
|
controlPanel,
|
||||||
metadata,
|
metadata,
|
||||||
transformProps,
|
transformProps,
|
||||||
loadChart: () => import('./FilterBox.jsx'),
|
loadChart: () => import('./FilterBox'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ export default class TimeTableChartPlugin extends ChartPlugin {
|
||||||
super({
|
super({
|
||||||
metadata,
|
metadata,
|
||||||
transformProps,
|
transformProps,
|
||||||
loadChart: () => import('./TimeTable.jsx'),
|
loadChart: () => import('./TimeTable'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,17 +21,19 @@ const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
|
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
|
||||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
|
||||||
const CopyPlugin = require('copy-webpack-plugin');
|
const CopyPlugin = require('copy-webpack-plugin');
|
||||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
||||||
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
|
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
|
||||||
const ManifestPlugin = require('webpack-manifest-plugin');
|
const {
|
||||||
|
WebpackManifestPlugin,
|
||||||
|
getCompilerHooks,
|
||||||
|
} = require('webpack-manifest-plugin');
|
||||||
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
||||||
const parsedArgs = require('yargs').argv;
|
const parsedArgs = require('yargs').argv;
|
||||||
const getProxyConfig = require('./webpack.proxy-config');
|
const getProxyConfig = require('./webpack.proxy-config');
|
||||||
const packageConfig = require('./package.json');
|
const packageConfig = require('./package');
|
||||||
|
|
||||||
// input dir
|
// input dir
|
||||||
const APP_DIR = path.resolve(__dirname, './');
|
const APP_DIR = path.resolve(__dirname, './');
|
||||||
|
|
@ -56,8 +58,8 @@ const output = {
|
||||||
publicPath: `${ASSET_BASE_URL}/static/assets/`,
|
publicPath: `${ASSET_BASE_URL}/static/assets/`,
|
||||||
};
|
};
|
||||||
if (isDevMode) {
|
if (isDevMode) {
|
||||||
output.filename = '[name].[hash:8].entry.js';
|
output.filename = '[name].[contenthash:8].entry.js';
|
||||||
output.chunkFilename = '[name].[hash:8].chunk.js';
|
output.chunkFilename = '[name].[contenthash:8].chunk.js';
|
||||||
} else if (nameChunks) {
|
} else if (nameChunks) {
|
||||||
output.filename = '[name].[chunkhash].entry.js';
|
output.filename = '[name].[chunkhash].entry.js';
|
||||||
output.chunkFilename = '[name].[chunkhash].chunk.js';
|
output.chunkFilename = '[name].[chunkhash].chunk.js';
|
||||||
|
|
@ -66,9 +68,17 @@ if (isDevMode) {
|
||||||
output.chunkFilename = '[chunkhash].chunk.js';
|
output.chunkFilename = '[chunkhash].chunk.js';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isDevMode) {
|
||||||
|
output.clean = true;
|
||||||
|
}
|
||||||
|
|
||||||
const plugins = [
|
const plugins = [
|
||||||
|
new webpack.ProvidePlugin({
|
||||||
|
process: 'process/browser',
|
||||||
|
}),
|
||||||
|
|
||||||
// creates a manifest.json mapping of name to hashed output used in template files
|
// creates a manifest.json mapping of name to hashed output used in template files
|
||||||
new ManifestPlugin({
|
new WebpackManifestPlugin({
|
||||||
publicPath: output.publicPath,
|
publicPath: output.publicPath,
|
||||||
seed: { app: 'superset' },
|
seed: { app: 'superset' },
|
||||||
// This enables us to include all relevant files for an entry
|
// This enables us to include all relevant files for an entry
|
||||||
|
|
@ -109,9 +119,10 @@ const plugins = [
|
||||||
|
|
||||||
// runs type checking on a separate process to speed up the build
|
// runs type checking on a separate process to speed up the build
|
||||||
new ForkTsCheckerWebpackPlugin({
|
new ForkTsCheckerWebpackPlugin({
|
||||||
eslint: true,
|
eslint: {
|
||||||
checkSyntacticErrors: true,
|
files: './src/**/*.{ts,tsx,js,jsx}',
|
||||||
memoryLimit: 4096,
|
memoryLimit: 4096,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
new CopyPlugin({
|
new CopyPlugin({
|
||||||
|
|
@ -141,17 +152,6 @@ if (!process.env.CI) {
|
||||||
plugins.push(new webpack.ProgressPlugin());
|
plugins.push(new webpack.ProgressPlugin());
|
||||||
}
|
}
|
||||||
|
|
||||||
// clean up built assets if not from dev-server
|
|
||||||
if (!isDevServer) {
|
|
||||||
plugins.push(
|
|
||||||
new CleanWebpackPlugin({
|
|
||||||
dry: false,
|
|
||||||
// required because the build directory is outside the frontend directory:
|
|
||||||
dangerouslyAllowCleanPatternsOutsideProject: true,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isDevMode) {
|
if (!isDevMode) {
|
||||||
// text loading (webpack 4+)
|
// text loading (webpack 4+)
|
||||||
plugins.push(
|
plugins.push(
|
||||||
|
|
@ -160,7 +160,6 @@ if (!isDevMode) {
|
||||||
chunkFilename: '[name].[chunkhash].chunk.css',
|
chunkFilename: '[name].[chunkhash].chunk.css',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
plugins.push(new OptimizeCSSAssetsPlugin());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const PREAMBLE = [path.join(APP_DIR, '/src/preamble.ts')];
|
const PREAMBLE = [path.join(APP_DIR, '/src/preamble.ts')];
|
||||||
|
|
@ -199,9 +198,6 @@ const babelLoader = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
node: {
|
|
||||||
fs: 'empty',
|
|
||||||
},
|
|
||||||
entry: {
|
entry: {
|
||||||
preamble: PREAMBLE,
|
preamble: PREAMBLE,
|
||||||
theme: path.join(APP_DIR, '/src/theme.ts'),
|
theme: path.join(APP_DIR, '/src/theme.ts'),
|
||||||
|
|
@ -268,13 +264,6 @@ const config = {
|
||||||
].join('|')})/`,
|
].join('|')})/`,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
// bundle large libraries separately
|
|
||||||
mathjs: {
|
|
||||||
name: 'mathjs',
|
|
||||||
test: /\/node_modules\/mathjs\//,
|
|
||||||
priority: 30,
|
|
||||||
enforce: true,
|
|
||||||
},
|
|
||||||
// viz thumbnails are used in `addSlice` and `explore` page
|
// viz thumbnails are used in `addSlice` and `explore` page
|
||||||
thumbnail: {
|
thumbnail: {
|
||||||
name: 'thumbnail',
|
name: 'thumbnail',
|
||||||
|
|
@ -284,6 +273,7 @@ const config = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
minimizer: [new CssMinimizerPlugin()],
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
modules: [APP_DIR, 'node_modules', ROOT_DIR],
|
modules: [APP_DIR, 'node_modules', ROOT_DIR],
|
||||||
|
|
@ -302,16 +292,21 @@ const config = {
|
||||||
},
|
},
|
||||||
extensions: ['.ts', '.tsx', '.js', '.jsx', '.yml'],
|
extensions: ['.ts', '.tsx', '.js', '.jsx', '.yml'],
|
||||||
symlinks: false,
|
symlinks: false,
|
||||||
|
fallback: {
|
||||||
|
fs: false,
|
||||||
|
vm: false,
|
||||||
|
path: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
context: APP_DIR, // to automatically find tsconfig.json
|
context: APP_DIR, // to automatically find tsconfig.json
|
||||||
module: {
|
module: {
|
||||||
// Uglifying mapbox-gl results in undefined errors, see
|
|
||||||
// https://github.com/mapbox/mapbox-gl-js/issues/4359#issuecomment-288001933
|
|
||||||
noParse: /(mapbox-gl)\.js$/,
|
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /datatables\.net.*/,
|
test: /datatables\.net.*/,
|
||||||
loader: 'imports-loader?define=>false',
|
loader: 'imports-loader',
|
||||||
|
options: {
|
||||||
|
additionalCode: 'var define = false;',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.tsx?$/,
|
test: /\.tsx?$/,
|
||||||
|
|
@ -388,52 +383,34 @@ const config = {
|
||||||
{
|
{
|
||||||
test: /\.png$/,
|
test: /\.png$/,
|
||||||
issuer: {
|
issuer: {
|
||||||
exclude: /\/src\/assets\/staticPages\//,
|
not: [/\/src\/assets\/staticPages\//],
|
||||||
},
|
},
|
||||||
loader: 'url-loader',
|
type: 'asset',
|
||||||
options: {
|
generator: {
|
||||||
limit: 10000,
|
filename: '[name].[contenthash:8].[ext]',
|
||||||
name: '[name].[hash:8].[ext]',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.png$/,
|
test: /\.png$/,
|
||||||
issuer: {
|
issuer: /\/src\/assets\/staticPages\//,
|
||||||
test: /\/src\/assets\/staticPages\//,
|
type: 'asset',
|
||||||
},
|
|
||||||
loader: 'url-loader',
|
|
||||||
options: {
|
|
||||||
limit: 150000, // Convert images < 150kb to base64 strings
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
|
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
|
||||||
issuer: {
|
issuer: /\.([jt])sx?$/,
|
||||||
test: /\.(j|t)sx?$/,
|
|
||||||
},
|
|
||||||
use: ['@svgr/webpack'],
|
use: ['@svgr/webpack'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(jpg|gif)$/,
|
test: /\.(jpg|gif)$/,
|
||||||
loader: 'file-loader',
|
type: 'asset/resource',
|
||||||
options: {
|
generator: {
|
||||||
name: '[name].[hash:8].[ext]',
|
filename: '[name].[contenthash:8].[ext]',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
/* for font-awesome */
|
/* for font-awesome */
|
||||||
{
|
{
|
||||||
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
test: /\.(woff|woff2|eot|ttf|otf)$/i,
|
||||||
loader: 'url-loader?limit=10000&mimetype=application/font-woff',
|
type: 'asset/resource',
|
||||||
options: {
|
|
||||||
esModule: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
|
||||||
loader: 'file-loader',
|
|
||||||
options: {
|
|
||||||
esModule: false,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.ya?ml$/,
|
test: /\.ya?ml$/,
|
||||||
|
|
@ -456,20 +433,15 @@ let proxyConfig = getProxyConfig();
|
||||||
if (isDevMode) {
|
if (isDevMode) {
|
||||||
config.devtool = 'eval-cheap-module-source-map';
|
config.devtool = 'eval-cheap-module-source-map';
|
||||||
config.devServer = {
|
config.devServer = {
|
||||||
before(app, server, compiler) {
|
onBeforeSetupMiddleware(devServer) {
|
||||||
// load proxy config when manifest updates
|
// load proxy config when manifest updates
|
||||||
const hook = compiler.hooks.webpackManifestPluginAfterEmit;
|
const { afterEmit } = getCompilerHooks(devServer.compiler);
|
||||||
hook.tap('ManifestPlugin', manifest => {
|
afterEmit.tap('ManifestPlugin', manifest => {
|
||||||
proxyConfig = getProxyConfig(manifest);
|
proxyConfig = getProxyConfig(manifest);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
historyApiFallback: true,
|
historyApiFallback: true,
|
||||||
hot: true,
|
hot: true,
|
||||||
injectClient: false,
|
|
||||||
injectHot: true,
|
|
||||||
inline: true,
|
|
||||||
stats: 'minimal',
|
|
||||||
overlay: true,
|
|
||||||
port: devserverPort,
|
port: devserverPort,
|
||||||
// Only serves bundled files from webpack-dev-server
|
// Only serves bundled files from webpack-dev-server
|
||||||
// and proxy everything else to Superset backend
|
// and proxy everything else to Superset backend
|
||||||
|
|
@ -477,7 +449,11 @@ if (isDevMode) {
|
||||||
// functions are called for every request
|
// functions are called for every request
|
||||||
() => proxyConfig,
|
() => proxyConfig,
|
||||||
],
|
],
|
||||||
contentBase: path.join(process.cwd(), '../static/assets'),
|
client: {
|
||||||
|
overlay: { errors: true, warnings: false },
|
||||||
|
logging: 'error',
|
||||||
|
},
|
||||||
|
static: path.join(process.cwd(), '../static/assets'),
|
||||||
};
|
};
|
||||||
|
|
||||||
// make sure to use @emotion/* modules in the root directory
|
// make sure to use @emotion/* modules in the root directory
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue