Revert "[webpack] setup lazy loading for all visualizations" (#5219)

* Revert "[explore] fix autocomplete on verbose names (#5204)"

This reverts commit d5ebc430c2.

* Revert "[webpack] setup lazy loading for all visualizations (#4727)"

This reverts commit de0aaf42ed.
This commit is contained in:
Chris Williams 2018-06-15 17:23:57 -07:00 committed by John Bodley
parent d5ebc430c2
commit 7b49b6c2de
20 changed files with 2551 additions and 1530 deletions

View File

@ -47,37 +47,23 @@ def parse_manifest_json():
global manifest global manifest
try: try:
with open(MANIFEST_FILE, 'r') as f: with open(MANIFEST_FILE, 'r') as f:
# the manifest inclues non-entry files manifest = json.load(f)
# we only need entries in templates
full_manifest = json.load(f)
manifest = full_manifest.get('entrypoints', {})
except Exception: except Exception:
pass pass
def get_js_manifest_files(filename): def get_manifest_file(filename):
if app.debug: if app.debug:
parse_manifest_json() parse_manifest_json()
entry_files = manifest.get(filename, {}) return '/static/assets/dist/' + manifest.get(filename, '')
return entry_files.get('js', [])
def get_css_manifest_files(filename):
if app.debug:
parse_manifest_json()
entry_files = manifest.get(filename, {})
return entry_files.get('css', [])
parse_manifest_json() parse_manifest_json()
@app.context_processor @app.context_processor
def get_manifest(): def get_js_manifest():
return dict( return dict(js_manifest=get_manifest_file)
js_manifest=get_js_manifest_files,
css_manifest=get_css_manifest_files,
)
################################################################# #################################################################

View File

@ -1,4 +1,3 @@
{ {
"presets" : ["airbnb", "react", "env"], "presets" : ["airbnb"],
"plugins": ["syntax-dynamic-import"],
} }

View File

@ -1,7 +1,6 @@
{ {
"extends": "airbnb", "extends": "airbnb",
"parser": "babel-eslint", "parserOptions":{
"parserOptions": {
"ecmaFeatures": { "ecmaFeatures": {
"experimentalObjectRestSpread": true "experimentalObjectRestSpread": true
} }

View File

@ -10,9 +10,11 @@
"scripts": { "scripts": {
"test": "mocha --require ignore-styles --compilers js:babel-core/register --require spec/helpers/browser.js --recursive spec/**/*_spec.*", "test": "mocha --require ignore-styles --compilers js:babel-core/register --require spec/helpers/browser.js --recursive spec/**/*_spec.*",
"cover": "babel-node node_modules/.bin/babel-istanbul cover _mocha -- --require ignore-styles spec/helpers/browser.js --recursive spec/**/*_spec.*", "cover": "babel-node node_modules/.bin/babel-istanbul cover _mocha -- --require ignore-styles spec/helpers/browser.js --recursive spec/**/*_spec.*",
"dev": "webpack --mode=development --colors --progress --debug --watch", "dev": "NODE_ENV=dev webpack --watch --colors --progress --debug --output-pathinfo --devtool eval-cheap-source-map",
"prod": "node --max_old_space_size=4096 webpack --mode=production --colors --progress", "dev-slow": "NODE_ENV=dev webpack --watch --colors --progress --debug --output-pathinfo --devtool inline-source-map",
"build": "webpack --mode=production --colors --progress", "dev-fast": "echo 'dev-fast in now replaced by dev'",
"prod": "NODE_ENV=production node --max_old_space_size=4096 ./node_modules/webpack/bin/webpack.js -p --colors --progress",
"build": "NODE_ENV=production webpack --colors --progress",
"lint": "eslint --ignore-path=.eslintignore --ext .js,.jsx .", "lint": "eslint --ignore-path=.eslintignore --ext .js,.jsx .",
"lint-fix": "eslint --fix --ignore-path=.eslintignore --ext .js,.jsx .", "lint-fix": "eslint --fix --ignore-path=.eslintignore --ext .js,.jsx .",
"sync-backend": "babel-node --presets env src/syncBackend.js" "sync-backend": "babel-node --presets env src/syncBackend.js"
@ -37,9 +39,6 @@
"bugs": { "bugs": {
"url": "https://github.com/apache/incubator-superset/issues" "url": "https://github.com/apache/incubator-superset/issues"
}, },
"engines": {
"node": ">= 6.11.5 <7.0.0 || >= 8.9.0"
},
"homepage": "http://superset.apache.org/", "homepage": "http://superset.apache.org/",
"dependencies": { "dependencies": {
"//": "known issues with react-bootstrap>=0.32", "//": "known issues with react-bootstrap>=0.32",
@ -117,21 +116,19 @@
"supercluster": "https://github.com/georgeke/supercluster/tarball/ac3492737e7ce98e07af679623aad452373bbc40", "supercluster": "https://github.com/georgeke/supercluster/tarball/ac3492737e7ce98e07af679623aad452373bbc40",
"underscore": "^1.8.3", "underscore": "^1.8.3",
"urijs": "^1.18.10", "urijs": "^1.18.10",
"viewport-mercator-project": "^5.0.0" "viewport-mercator-project": "^5.0.0",
"webpack-cli": "^2.1.4"
}, },
"devDependencies": { "devDependencies": {
"babel-cli": "^6.14.0", "babel-cli": "^6.14.0",
"babel-core": "^6.10.4", "babel-core": "^6.10.4",
"babel-eslint": "^8.2.2",
"babel-istanbul": "^0.12.2", "babel-istanbul": "^0.12.2",
"babel-loader": "^7.1.4", "babel-loader": "^7.0.0",
"babel-plugin-css-modules-transform": "^1.1.0", "babel-plugin-css-modules-transform": "^1.1.0",
"babel-plugin-dynamic-import-node": "^1.2.0",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-polyfill": "^6.23.0", "babel-polyfill": "^6.23.0",
"babel-preset-airbnb": "^2.1.1", "babel-preset-airbnb": "^2.1.1",
"chai": "^4.0.2", "chai": "^4.0.2",
"clean-webpack-plugin": "^0.1.19", "clean-webpack-plugin": "^0.1.16",
"css-loader": "^0.28.0", "css-loader": "^0.28.0",
"enzyme": "^2.0.0", "enzyme": "^2.0.0",
"eslint": "^4.19.0", "eslint": "^4.19.0",
@ -140,7 +137,8 @@
"eslint-plugin-jsx-a11y": "^5.1.1", "eslint-plugin-jsx-a11y": "^5.1.1",
"eslint-plugin-react": "^7.0.1", "eslint-plugin-react": "^7.0.1",
"exports-loader": "^0.7.0", "exports-loader": "^0.7.0",
"file-loader": "^1.1.11", "extract-text-webpack-plugin": "3.0.2",
"file-loader": "^0.11.1",
"github-changes": "^1.0.4", "github-changes": "^1.0.4",
"ignore-styles": "^5.0.1", "ignore-styles": "^5.0.1",
"imports-loader": "^0.7.1", "imports-loader": "^0.7.1",
@ -148,8 +146,7 @@
"jsdom": "9.12.0", "jsdom": "9.12.0",
"json-loader": "^0.5.4", "json-loader": "^0.5.4",
"less": "^2.6.1", "less": "^2.6.1",
"less-loader": "^4.1.0", "less-loader": "^4.0.3",
"mini-css-extract-plugin": "^0.4.0",
"mocha": "^3.2.0", "mocha": "^3.2.0",
"npm-check-updates": "^2.14.0", "npm-check-updates": "^2.14.0",
"react-addons-test-utils": "^15.6.2", "react-addons-test-utils": "^15.6.2",
@ -160,10 +157,10 @@
"style-loader": "^0.21.0", "style-loader": "^0.21.0",
"transform-loader": "^0.2.3", "transform-loader": "^0.2.3",
"uglifyjs-webpack-plugin": "^1.1.0", "uglifyjs-webpack-plugin": "^1.1.0",
"url-loader": "^1.0.1", "url-loader": "^0.6.2",
"webpack": "^4.6.0", "//": "Known issues with >=4",
"webpack-assets-manifest": "^3.0.1", "webpack": "^3.10.0",
"webpack-cli": "^2.0.10", "webpack-manifest-plugin": "2.0.3",
"webpack-sources": "^1.1.0" "webworkify-webpack": "2.1.2"
} }
} }

View File

@ -3,12 +3,7 @@ import 'babel-polyfill';
import chai from 'chai'; import chai from 'chai';
import jsdom from 'jsdom'; import jsdom from 'jsdom';
require('babel-register')({ require('babel-register')();
// NOTE: If `dynamic-import-node` is in .babelrc alongside
// `syntax-dynamic-import` it breaks webpack's bundle splitting capability.
// So only load during runtime on the node-side (in tests)
plugins: ['dynamic-import-node'],
});
const exposedProperties = ['window', 'navigator', 'document']; const exposedProperties = ['window', 'navigator', 'document'];

View File

@ -38,10 +38,10 @@ describe('Chart', () => {
<Chart {...mockedProps} />, <Chart {...mockedProps} />,
); );
}); });
describe('renderVis', () => { describe('renderViz', () => {
let stub; let stub;
beforeEach(() => { beforeEach(() => {
stub = sinon.stub(wrapper.instance(), 'renderVis'); stub = sinon.stub(wrapper.instance(), 'renderViz');
}); });
afterEach(() => { afterEach(() => {
stub.restore(); stub.restore();

View File

@ -10,7 +10,7 @@ import Loading from '../components/Loading';
import { Logger, LOG_ACTIONS_RENDER_EVENT } from '../logger'; import { Logger, LOG_ACTIONS_RENDER_EVENT } from '../logger';
import StackTraceMessage from '../components/StackTraceMessage'; import StackTraceMessage from '../components/StackTraceMessage';
import RefreshChartOverlay from '../components/RefreshChartOverlay'; import RefreshChartOverlay from '../components/RefreshChartOverlay';
import visPromiseLookup from '../visualizations'; import visMap from '../visualizations';
import sandboxedEval from '../modules/sandbox'; import sandboxedEval from '../modules/sandbox';
import './chart.css'; import './chart.css';
@ -58,11 +58,7 @@ const defaultProps = {
class Chart extends React.PureComponent { class Chart extends React.PureComponent {
constructor(props) { constructor(props) {
super(props); super(props);
// visualizations are lazy-loaded with promises that resolve to a renderVis function this.state = {};
this.state = {
renderVis: null,
};
// these properties are used by visualizations // these properties are used by visualizations
this.annotationData = props.annotationData; this.annotationData = props.annotationData;
this.containerId = props.containerId; this.containerId = props.containerId;
@ -76,20 +72,15 @@ class Chart extends React.PureComponent {
this.headerHeight = this.headerHeight.bind(this); this.headerHeight = this.headerHeight.bind(this);
this.height = this.height.bind(this); this.height = this.height.bind(this);
this.width = this.width.bind(this); this.width = this.width.bind(this);
this.visPromise = null;
} }
componentDidMount() { componentDidMount() {
if (this.props.triggerQuery) { if (this.props.triggerQuery) {
this.props.actions.runQuery( this.props.actions.runQuery(this.props.formData, false,
this.props.formData,
false,
this.props.timeout, this.props.timeout,
this.props.chartKey, this.props.chartKey,
); );
} }
this.loadAsyncVis(this.props.vizType);
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
@ -98,31 +89,23 @@ class Chart extends React.PureComponent {
this.selector = `#${this.containerId}`; this.selector = `#${this.containerId}`;
this.formData = nextProps.formData; this.formData = nextProps.formData;
this.datasource = nextProps.datasource; this.datasource = nextProps.datasource;
if (nextProps.vizType !== this.props.vizType) {
this.setState(() => ({ renderVis: null }));
this.loadAsyncVis(nextProps.vizType);
}
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
if ( if (
this.props.queryResponse && this.props.queryResponse &&
['success', 'rendered'].indexOf(this.props.chartStatus) > -1 && ['success', 'rendered'].indexOf(this.props.chartStatus) > -1 &&
!this.props.queryResponse.error && !this.props.queryResponse.error && (
(prevProps.annotationData !== this.props.annotationData || prevProps.annotationData !== this.props.annotationData ||
prevProps.queryResponse !== this.props.queryResponse || prevProps.queryResponse !== this.props.queryResponse ||
prevProps.height !== this.props.height || prevProps.height !== this.props.height ||
prevProps.width !== this.props.width || prevProps.width !== this.props.width ||
prevProps.lastRendered !== this.props.lastRendered) prevProps.lastRendered !== this.props.lastRendered)
) { ) {
this.renderVis(); this.renderViz();
} }
} }
componentWillUnmount() {
this.visPromise = null;
}
getFilters() { getFilters() {
return this.props.getFilters(); return this.props.getFilters();
} }
@ -131,22 +114,6 @@ class Chart extends React.PureComponent {
this.setState({ tooltip }); this.setState({ tooltip });
} }
loadAsyncVis(visType) {
this.visPromise = visPromiseLookup[visType];
this.visPromise()
.then((renderVis) => {
// ensure Component is still mounted
if (this.visPromise) {
this.setState({ renderVis }, this.renderVis);
}
})
.catch((error) => {
console.error(error); // eslint-disable-line
this.props.actions.chartRenderingFailed(error, this.props.chartKey);
});
}
addFilter(col, vals, merge = true, refresh = true) { addFilter(col, vals, merge = true, refresh = true) {
this.props.addFilter(col, vals, merge, refresh); this.props.addFilter(col, vals, merge, refresh);
} }
@ -218,77 +185,69 @@ class Chart extends React.PureComponent {
return null; return null;
} }
renderVis() { renderViz() {
// check that we have the render function and data const viz = visMap[this.props.vizType];
if (this.state.renderVis && ['success', 'rendered'].indexOf(this.props.chartStatus) > -1) { const fd = this.props.formData;
const fd = this.props.formData; const qr = this.props.queryResponse;
const qr = this.props.queryResponse; const renderStart = Logger.getTimestamp();
const renderStart = Logger.getTimestamp(); try {
// Executing user-defined data mutator function
try { if (fd.js_data) {
// Executing user-defined data mutator function qr.data = sandboxedEval(fd.js_data)(qr.data);
if (fd.js_data) {
qr.data = sandboxedEval(fd.js_data)(qr.data);
}
// [re]rendering the visualization
this.state.renderVis(this, qr, this.props.setControlValue);
Logger.append(LOG_ACTIONS_RENDER_EVENT, {
label: this.props.chartKey,
vis_type: this.props.vizType,
start_offset: renderStart,
duration: Logger.getTimestamp() - renderStart,
});
this.props.actions.chartRenderingSucceeded(this.props.chartKey);
} catch (e) {
console.error(e); // eslint-disable-line
this.props.actions.chartRenderingFailed(e, this.props.chartKey);
} }
// [re]rendering the visualization
viz(this, qr, this.props.setControlValue);
Logger.append(LOG_ACTIONS_RENDER_EVENT, {
label: this.props.chartKey,
vis_type: this.props.vizType,
start_offset: renderStart,
duration: Logger.getTimestamp() - renderStart,
});
this.props.actions.chartRenderingSucceeded(this.props.chartKey);
} catch (e) {
this.props.actions.chartRenderingFailed(e, this.props.chartKey);
} }
} }
render() { render() {
const isLoading = this.props.chartStatus === 'loading' || !this.state.renderVis; const isLoading = this.props.chartStatus === 'loading';
return ( return (
<div className={`token col-md-12 ${isLoading ? 'is-loading' : ''}`}> <div className={`token col-md-12 ${isLoading ? 'is-loading' : ''}`}>
{this.renderTooltip()} {this.renderTooltip()}
{isLoading &&
{isLoading && <Loading size={25} />} <Loading size={25} />
}
{this.props.chartAlert && ( {this.props.chartAlert &&
<StackTraceMessage <StackTraceMessage
message={this.props.chartAlert} message={this.props.chartAlert}
queryResponse={this.props.queryResponse} queryResponse={this.props.queryResponse}
/> />
)} }
{!isLoading && {!isLoading &&
!this.props.chartAlert && !this.props.chartAlert &&
this.props.refreshOverlayVisible && this.props.refreshOverlayVisible &&
!this.props.errorMessage && !this.props.errorMessage &&
this.container && ( this.container &&
<RefreshChartOverlay <RefreshChartOverlay
height={this.height()} height={this.height()}
width={this.width()} width={this.width()}
onQuery={this.props.onQuery} onQuery={this.props.onQuery}
onDismiss={this.props.onDismissRefreshOverlay} onDismiss={this.props.onDismissRefreshOverlay}
/> />
)} }
{!isLoading && !this.props.chartAlert &&
{!isLoading && <ChartBody
!this.props.chartAlert && ( containerId={this.containerId}
<ChartBody vizType={this.props.vizType}
containerId={this.containerId} height={this.height}
vizType={this.props.vizType} width={this.width}
height={this.height} faded={this.props.refreshOverlayVisible && !this.props.errorMessage}
width={this.width} ref={(inner) => {
faded={this.props.refreshOverlayVisible && !this.props.errorMessage} this.container = inner;
ref={(inner) => { }}
this.container = inner; />
}} }
/>
)}
</div> </div>
); );
} }

View File

@ -8,15 +8,15 @@ const defaultProps = {
size: 25, size: 25,
}; };
export default function Loading({ size }) { export default function Loading(props) {
return ( return (
<img <img
className="loading" className="loading"
alt="Loading..." alt="Loading..."
src="/static/assets/images/loading.gif" src="/static/assets/images/loading.gif"
style={{ style={{
width: size, width: props.size,
height: size, height: props.size,
padding: 0, padding: 0,
margin: 0, margin: 0,
position: 'absolute', position: 'absolute',

View File

@ -131,7 +131,7 @@ class SliceHeader extends React.PureComponent {
</a> </a>
} }
{this.props.sliceCanEdit && {this.props.sliceCanEdit &&
<a href={slice.edit_url} target="_blank" rel="noopener noreferrer"> <a href={slice.edit_url} target="_blank">
<TooltipWrapper <TooltipWrapper
placement="top" placement="top"
label="edit" label="edit"

View File

@ -94,8 +94,8 @@ const propTypes = {
}; };
class DeckGLScatter extends React.PureComponent { class DeckGLScatter extends React.PureComponent {
/* eslint-disable-next-line react/sort-comp */ /* eslint-disable no-unused-vars */
static getDerivedStateFromProps(nextProps) { static getDerivedStateFromProps(nextProps, prevState) {
const fd = nextProps.slice.formData; const fd = nextProps.slice.formData;
const timeGrain = fd.time_grain_sqla || fd.granularity || 'PT1M'; const timeGrain = fd.time_grain_sqla || fd.granularity || 'PT1M';

View File

@ -59,8 +59,8 @@ const propTypes = {
}; };
class DeckGLScreenGrid extends React.PureComponent { class DeckGLScreenGrid extends React.PureComponent {
/* eslint-disable-next-line react/sort-comp */ /* eslint-disable no-unused-vars */
static getDerivedStateFromProps(nextProps) { static getDerivedStateFromProps(nextProps, prevState) {
const fd = nextProps.slice.formData; const fd = nextProps.slice.formData;
const timeGrain = fd.time_grain_sqla || fd.granularity || 'PT1M'; const timeGrain = fd.time_grain_sqla || fd.granularity || 'PT1M';

View File

@ -1,4 +1,6 @@
/* eslint-disable global-require */ /* eslint-disable global-require */
import nvd3Vis from './nvd3_vis';
import lineMulti from './line_multi';
// You ***should*** use these to reference viz_types in code // You ***should*** use these to reference viz_types in code
export const VIZ_TYPES = { export const VIZ_TYPES = {
@ -52,91 +54,54 @@ export const VIZ_TYPES = {
rose: 'rose', rose: 'rose',
}; };
const loadVis = promise =>
promise.then((module) => {
const defaultExport = module.default || module;
// deckgl visualizations don't use esModules, fix it?
return defaultExport.default || defaultExport;
});
const loadNvd3 = () => loadVis(import(/* webpackChunkName: "nvd3_vis" */ './nvd3_vis.js'));
const vizMap = { const vizMap = {
[VIZ_TYPES.area]: loadNvd3, [VIZ_TYPES.area]: nvd3Vis,
[VIZ_TYPES.bar]: loadNvd3, [VIZ_TYPES.bar]: nvd3Vis,
[VIZ_TYPES.big_number]: () => [VIZ_TYPES.big_number]: require('./big_number.js'),
loadVis(import(/* webpackChunkName: 'big_number' */ './big_number.js')), [VIZ_TYPES.big_number_total]: require('./big_number.js'),
[VIZ_TYPES.big_number_total]: () => [VIZ_TYPES.box_plot]: nvd3Vis,
loadVis(import(/* webpackChunkName: "big_number" */ './big_number.js')), [VIZ_TYPES.bubble]: nvd3Vis,
[VIZ_TYPES.box_plot]: loadNvd3, [VIZ_TYPES.bullet]: nvd3Vis,
[VIZ_TYPES.bubble]: loadNvd3, [VIZ_TYPES.cal_heatmap]: require('./cal_heatmap.js'),
[VIZ_TYPES.bullet]: loadNvd3, [VIZ_TYPES.compare]: nvd3Vis,
[VIZ_TYPES.cal_heatmap]: () => [VIZ_TYPES.directed_force]: require('./directed_force.js'),
loadVis(import(/* webpackChunkName: "cal_heatmap" */ './cal_heatmap.js')), [VIZ_TYPES.chord]: require('./chord.jsx'),
[VIZ_TYPES.compare]: loadNvd3, [VIZ_TYPES.dist_bar]: nvd3Vis,
[VIZ_TYPES.directed_force]: () => [VIZ_TYPES.filter_box]: require('./filter_box.jsx'),
loadVis(import(/* webpackChunkName: "directed_force" */ './directed_force.js')), [VIZ_TYPES.heatmap]: require('./heatmap.js'),
[VIZ_TYPES.chord]: () => loadVis(import(/* webpackChunkName: "chord" */ './chord.jsx')), [VIZ_TYPES.histogram]: require('./histogram.js'),
[VIZ_TYPES.dist_bar]: loadNvd3, [VIZ_TYPES.horizon]: require('./horizon.js'),
[VIZ_TYPES.filter_box]: () => [VIZ_TYPES.iframe]: require('./iframe.js'),
loadVis(import(/* webpackChunkName: "filter_box" */ './filter_box.jsx')), [VIZ_TYPES.line]: nvd3Vis,
[VIZ_TYPES.heatmap]: () => loadVis(import(/* webpackChunkName: "heatmap" */ './heatmap.js')), [VIZ_TYPES.line_multi]: lineMulti,
[VIZ_TYPES.histogram]: () => [VIZ_TYPES.time_pivot]: nvd3Vis,
loadVis(import(/* webpackChunkName: "histogram" */ './histogram.js')), [VIZ_TYPES.mapbox]: require('./mapbox.jsx'),
[VIZ_TYPES.horizon]: () => loadVis(import(/* webpackChunkName: "horizon" */ './horizon.js')), [VIZ_TYPES.markup]: require('./markup.js'),
[VIZ_TYPES.iframe]: () => loadVis(import(/* webpackChunkName: "iframe" */ './iframe.js')), [VIZ_TYPES.para]: require('./parallel_coordinates.js'),
[VIZ_TYPES.line]: loadNvd3, [VIZ_TYPES.pie]: nvd3Vis,
[VIZ_TYPES.line_multi]: () => [VIZ_TYPES.pivot_table]: require('./pivot_table.js'),
loadVis(import(/* webpackChunkName: "line_multi" */ './line_multi.js')), [VIZ_TYPES.sankey]: require('./sankey.js'),
[VIZ_TYPES.time_pivot]: loadNvd3, [VIZ_TYPES.separator]: require('./markup.js'),
[VIZ_TYPES.mapbox]: () => loadVis(import(/* webpackChunkName: "mapbox" */ './mapbox.jsx')), [VIZ_TYPES.sunburst]: require('./sunburst.js'),
[VIZ_TYPES.markup]: () => loadVis(import(/* webpackChunkName: "markup" */ './markup.js')), [VIZ_TYPES.table]: require('./table.js'),
[VIZ_TYPES.para]: () => [VIZ_TYPES.time_table]: require('./time_table.jsx'),
loadVis(import(/* webpackChunkName: "parallel_coordinates" */ './parallel_coordinates.js')), [VIZ_TYPES.treemap]: require('./treemap.js'),
[VIZ_TYPES.pie]: loadNvd3, [VIZ_TYPES.country_map]: require('./country_map.js'),
[VIZ_TYPES.pivot_table]: () => [VIZ_TYPES.word_cloud]: require('./word_cloud.js'),
loadVis(import(/* webpackChunkName: "pivot_table" */ './pivot_table.js')), [VIZ_TYPES.world_map]: require('./world_map.js'),
[VIZ_TYPES.sankey]: () => loadVis(import(/* webpackChunkName: "sankey" */ './sankey.js')), [VIZ_TYPES.dual_line]: nvd3Vis,
[VIZ_TYPES.separator]: () => loadVis(import(/* webpackChunkName: "markup" */ './markup.js')), [VIZ_TYPES.event_flow]: require('./EventFlow.jsx'),
[VIZ_TYPES.sunburst]: () => loadVis(import(/* webpackChunkName: "sunburst" */ './sunburst.js')), [VIZ_TYPES.paired_ttest]: require('./paired_ttest.jsx'),
[VIZ_TYPES.table]: () => loadVis(import(/* webpackChunkName: "table" */ './table.js')), [VIZ_TYPES.partition]: require('./partition.js'),
[VIZ_TYPES.time_table]: () => [VIZ_TYPES.deck_scatter]: require('./deckgl/layers/scatter.jsx').default,
loadVis(import(/* webpackChunkName: "time_table" */ './time_table.jsx')), [VIZ_TYPES.deck_screengrid]: require('./deckgl/layers/screengrid.jsx').default,
[VIZ_TYPES.treemap]: () => loadVis(import(/* webpackChunkName: "treemap" */ './treemap.js')), [VIZ_TYPES.deck_grid]: require('./deckgl/layers/grid.jsx').default,
[VIZ_TYPES.country_map]: () => [VIZ_TYPES.deck_hex]: require('./deckgl/layers/hex.jsx').default,
loadVis(import(/* webpackChunkName: "country_map" */ './country_map.js')), [VIZ_TYPES.deck_path]: require('./deckgl/layers/path.jsx').default,
[VIZ_TYPES.word_cloud]: () => [VIZ_TYPES.deck_geojson]: require('./deckgl/layers/geojson.jsx').default,
loadVis(import(/* webpackChunkName: "word_cloud" */ './word_cloud.js')), [VIZ_TYPES.deck_arc]: require('./deckgl/layers/arc.jsx').default,
[VIZ_TYPES.world_map]: () => [VIZ_TYPES.deck_polygon]: require('./deckgl/layers/polygon.jsx').default,
loadVis(import(/* webpackChunkName: "world_map" */ './world_map.js')), [VIZ_TYPES.deck_multi]: require('./deckgl/multi.jsx'),
[VIZ_TYPES.dual_line]: loadNvd3, [VIZ_TYPES.rose]: require('./rose.js'),
[VIZ_TYPES.event_flow]: () =>
loadVis(import(/* webpackChunkName: "EventFlow" */ './EventFlow.jsx')),
[VIZ_TYPES.paired_ttest]: () =>
loadVis(import(/* webpackChunkName: "paired_ttest" */ './paired_ttest.jsx')),
[VIZ_TYPES.partition]: () =>
loadVis(import(/* webpackChunkName: "partition" */ './partition.js')),
[VIZ_TYPES.deck_scatter]: () =>
loadVis(import(/* webpackChunkName: "deckgl/layers/scatter" */ './deckgl/layers/scatter.jsx')),
[VIZ_TYPES.deck_screengrid]: () =>
loadVis(
import(/* webpackChunkName: "deckgl/layers/screengrid" */ './deckgl/layers/screengrid.jsx'),
),
[VIZ_TYPES.deck_grid]: () =>
loadVis(import(/* webpackChunkName: "deckgl/layers/grid" */ './deckgl/layers/grid.jsx')),
[VIZ_TYPES.deck_hex]: () =>
loadVis(import(/* webpackChunkName: "deckgl/layers/hex" */ './deckgl/layers/hex.jsx')),
[VIZ_TYPES.deck_path]: () =>
loadVis(import(/* webpackChunkName: "deckgl/layers/path" */ './deckgl/layers/path.jsx')),
[VIZ_TYPES.deck_geojson]: () =>
loadVis(import(/* webpackChunkName: "deckgl/layers/geojson" */ './deckgl/layers/geojson.jsx')),
[VIZ_TYPES.deck_arc]: () =>
loadVis(import(/* webpackChunkName: "deckgl/layers/arc" */ './deckgl/layers/arc.jsx')),
[VIZ_TYPES.deck_polygon]: () =>
loadVis(import(/* webpackChunkName: "deckgl/layers/polygon" */ './deckgl/layers/polygon.jsx')),
[VIZ_TYPES.deck_multi]: () =>
loadVis(import(/* webpackChunkName: "deckgl/multi" */ './deckgl/multi.jsx')),
[VIZ_TYPES.rose]: () => loadVis(import(/* webpackChunkName: "rose" */ './rose.js')),
}; };
export default vizMap; export default vizMap;

View File

@ -17,13 +17,10 @@ function markupWidget(slice, payload) {
} }
const iframeId = `if__${slice.containerId}`; const iframeId = `if__${slice.containerId}`;
const stylesheets = payload.data.theme_css.map(
href => `<link rel="stylesheet" type="text/css" href="${href}" />`,
);
const html = ` const html = `
<html> <html>
<head> <head>
${stylesheets} <link rel="stylesheet" type="text/css" href="${payload.data.theme_css}" />
</head> </head>
<body style="background-color: transparent;"> <body style="background-color: transparent;">
${payload.data.html} ${payload.data.html}

View File

@ -71,9 +71,7 @@ function viz(slice, payload) {
<MetricOption metric={metricData} url={url} showFormula={false} openInNewWindow /> <MetricOption metric={metricData} url={url} showFormula={false} openInNewWindow />
); );
} else { } else {
leftCell = url ? leftCell = url ? <a href={url} target="_blank">{metricLabel}</a> : metric;
<a href={url} target="_blank" rel="noopener noreferrer">{metricLabel}</a>
: metric;
} }
const row = { metric: leftCell }; const row = { metric: leftCell };
fd.column_collection.forEach((column) => { fd.column_collection.forEach((column) => {

View File

@ -1,7 +1,8 @@
const webpack = require('webpack');
const path = require('path'); const path = require('path');
const ManifestPlugin = require('webpack-manifest-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const ExtractTextPlugin = require('extract-text-webpack-plugin');
const WebpackAssetsManifest = require('webpack-assets-manifest');
// input dir // input dir
const APP_DIR = path.resolve(__dirname, './'); const APP_DIR = path.resolve(__dirname, './');
@ -9,8 +10,6 @@ const APP_DIR = path.resolve(__dirname, './');
// output dir // output dir
const BUILD_DIR = path.resolve(__dirname, './dist'); const BUILD_DIR = path.resolve(__dirname, './dist');
const isDevMode = process.env.NODE_ENV !== 'production';
const config = { const config = {
node: { node: {
fs: 'empty', fs: 'empty',
@ -27,23 +26,20 @@ const config = {
}, },
output: { output: {
path: BUILD_DIR, path: BUILD_DIR,
publicPath: '/static/assets/dist/', // necessary for lazy-loaded chunks
filename: '[name].[chunkhash].entry.js', filename: '[name].[chunkhash].entry.js',
chunkFilename: '[name].[chunkhash].chunk.js', chunkFilename: '[name].[chunkhash].entry.js',
},
optimization: {
splitChunks: {
chunks: 'all',
automaticNameDelimiter: '-',
},
}, },
resolve: { resolve: {
extensions: ['.js', '.jsx'], extensions: [
'.js',
'.jsx',
],
alias: {
webworkify: 'webworkify-webpack',
},
}, },
module: { module: {
// uglyfying 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.*/,
@ -53,20 +49,32 @@ const config = {
test: /\.jsx?$/, test: /\.jsx?$/,
exclude: /node_modules/, exclude: /node_modules/,
loader: 'babel-loader', loader: 'babel-loader',
query: {
presets: [
'airbnb',
'env',
'react',
],
},
}, },
// Extract css files
{ {
test: /\.css$/, test: /\.css$/,
include: APP_DIR, include: APP_DIR,
use: [isDevMode ? MiniCssExtractPlugin.loader : 'style-loader', 'css-loader'], loader: ExtractTextPlugin.extract({
use: ['css-loader'],
fallback: 'style-loader',
}),
}, },
// Optionally extract less files
// or any other compile-to-css language
{ {
test: /\.less$/, test: /\.less$/,
include: APP_DIR, include: APP_DIR,
use: [ loader: ExtractTextPlugin.extract({
isDevMode ? MiniCssExtractPlugin.loader : 'style-loader', use: ['css-loader', 'less-loader'],
'css-loader', fallback: 'style-loader',
'less-loader', }),
],
}, },
/* for css linking images */ /* for css linking images */
{ {
@ -98,21 +106,27 @@ const config = {
'react/lib/ReactContext': true, 'react/lib/ReactContext': true,
}, },
plugins: [ plugins: [
// creates a manifest.json mapping of name to hashed output used in template files new ManifestPlugin(),
new WebpackAssetsManifest({
publicPath: true,
entrypoints: true, // this enables us to include all relevant files for an entry
}),
// create fresh dist/ upon build
new CleanWebpackPlugin(['dist']), new CleanWebpackPlugin(['dist']),
new webpack.DefinePlugin({
// text loading (webpack 4+) 'process.env': {
new MiniCssExtractPlugin({ NODE_ENV: JSON.stringify(process.env.NODE_ENV),
filename: '[name].[chunkhash].entry.css', },
chunkFilename: '[name].[chunkhash].chunk.css',
}), }),
new ExtractTextPlugin('[name].[chunkhash].css'),
], ],
}; };
if (process.env.NODE_ENV === 'production') {
// Using settings suggested in https://github.com/webpack/webpack/issues/537
const UJSplugin = new webpack.optimize.UglifyJsPlugin({
sourceMap: false,
minimize: true,
parallel: {
cache: true,
workers: 4,
},
compress: false,
});
config.plugins.push(UJSplugin);
}
module.exports = config; module.exports = config;

File diff suppressed because it is too large Load Diff

View File

@ -3,14 +3,10 @@
{% block head_css %} {% block head_css %}
{{super()}} {{super()}}
<link rel="icon" type="image/png" href="/static/assets/images/favicon.png"> <link rel="icon" type="image/png" href="/static/assets/images/favicon.png">
{% for entry in css_manifest('theme') %} <link rel="stylesheet" type="text/css" href="{{ js_manifest('theme.css') }}" />
<link rel="stylesheet" type="text/css" href="{{ entry }}" />
{% endfor %}
{% endblock %} {% endblock %}
{% block tail_js %} {% block tail_js %}
{{super()}} {{super()}}
{% for entry in js_manifest('common') %} <script src="{{ js_manifest('common.js') }}"></script>
<script src="{{ entry }}"></script>
{% endfor %}
{% endblock %} {% endblock %}

View File

@ -14,25 +14,15 @@
{% block head_css %} {% block head_css %}
<link rel="icon" type="image/png" href="/static/assets/images/favicon.png"> <link rel="icon" type="image/png" href="/static/assets/images/favicon.png">
<link rel="stylesheet" type="text/css" href="/static/appbuilder/css/flags/flags16.css" /> <link rel="stylesheet" type="text/css" href="/static/appbuilder/css/flags/flags16.css" />
<link rel="stylesheet" type="text/css" href="{{ js_manifest('theme.css') }}" />
<link rel="stylesheet" type="text/css" href="/static/appbuilder/css/font-awesome.min.css"> <link rel="stylesheet" type="text/css" href="/static/appbuilder/css/font-awesome.min.css">
{% for entry in css_manifest('theme') %}
<link rel="stylesheet" type="text/css" href="{{ entry }}" />
{% endfor %}
{% if entry %} {% if entry %}
{% set entry_files = css_manifest(entry) %} <link rel="stylesheet" type="text/css" href="{{ js_manifest(entry + '.css') }}" />
{% for entry in entry_files %}
<link rel="stylesheet" type="text/css" href="{{ entry }}" />
{% endfor %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block head_js %}
{% for entry in js_manifest('common') %} <script src="{{ js_manifest('common.js') }}"></script>
<script src="{{ entry }}"></script> {% endblock %}
{% endfor %}
<input <input
type="hidden" type="hidden"
name="csrf_token" name="csrf_token"
@ -76,10 +66,7 @@
</div> </div>
{% block tail_js %} {% block tail_js %}
{% if entry %} {% if entry %}
{% set entry_files = js_manifest(entry) %} <script src="{{ js_manifest(entry + '.js') }}"></script>
{% for entry in entry_files %}
<script src="{{ entry }}"></script>
{% endfor %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}
</body> </body>

View File

@ -1,5 +1,5 @@
{% block tail_js %} {% block tail_js %}
{% for entry in js_manifest(filename) %} <script
<script src="{{ entry }}"></script> src="{{ js_manifest(filename + '.js') }}">
{% endfor %} </script>
{% endblock %} {% endblock %}

View File

@ -36,7 +36,7 @@ import simplejson as json
from six import string_types, text_type from six import string_types, text_type
from six.moves import cPickle as pkl, reduce from six.moves import cPickle as pkl, reduce
from superset import app, cache, get_css_manifest_files, utils from superset import app, cache, get_manifest_file, utils
from superset.utils import DTTM_ALIAS, JS_MAX_INTEGER, merge_extra_filters from superset.utils import DTTM_ALIAS, JS_MAX_INTEGER, merge_extra_filters
@ -696,7 +696,7 @@ class MarkupViz(BaseViz):
code = self.form_data.get('code', '') code = self.form_data.get('code', '')
if markup_type == 'markdown': if markup_type == 'markdown':
code = markdown(code) code = markdown(code)
return dict(html=code, theme_css=get_css_manifest_files('theme')) return dict(html=code, theme_css=get_manifest_file('theme.css'))
class SeparatorViz(MarkupViz): class SeparatorViz(MarkupViz):